Устранение утечек памяти в классе new и удаление операторов C ++ - PullRequest
1 голос
/ 12 февраля 2012

Мне очень нравится использовать операторы new и delete в C ++, но часто возникают проблемы с вызовом delete позже в коде программы.

Например, в следующем коде:

class Foo {
public:
    string *ace;
    Foo();
    ~Foo();
};
Foo::Foo() {
    ace = new string;
}
Foo::~Foo() {
    delete ace;
}
void UI::ButtonPressed() { //Happens when an event is triggered
    Foo *foo = new Foo;
    ui->label = ace; //Set some text on the GUI
    delete foo; //Calls the destructor, deleting "ace" and removing it from the GUI window
}

Я могу объявить строку new, но когда я delete, она удаляет значение из формы графического интерфейса, поскольку эта строка была удалена.

Есть ли способдля меня, чтобы удалить эту выделенную строку как-нибудь позже?

Я не хочу объявлять ее как глобальную переменную, а затем delete в последней строке исходного кода программы.Я никогда не смогу назвать delete, но, как меня учили, это плохо и приводит к утечкам памяти.

Ответы [ 4 ]

1 голос
/ 12 февраля 2012

Ну, о вашем коде можно многое сказать. Некоторые вещи уже были сказаны, например, что вы должны сделать строку нормальным членом, чтобы проблема выделения / отключения не устранялась полностью (это общее правило для программ на C ++: если вам абсолютно не нужно использовать динамическое выделение, то не делайте, точка ). Кроме того, использование подходящего интеллектуального указателя сделает управление памятью за вас (также общее правило в C ++: не управляйте динамическим распределением самостоятельно, если вам не нужно).

Однако давайте представим, что вам нужно использовать динамическое распределение, и вы должны использовать необработанные указатели и прямые new и delete здесь. Затем приходит другое важное правило (которое на самом деле не является специфическим правилом C ++, а общим правилом OO): не делайте члена открытым. Сделайте его закрытым членом и предложите функцию открытого члена для его настройки. Эта общедоступная функция-член может затем правильно удалить старый объект перед назначением указателя на новый. Обратите внимание, что как только вы присвоили указатель, если вы не сохранили старое значение в другом месте, старое значение будет потеряно навсегда, и если объект не был удален до этого момента, вы не сможете удалить его позже.

Вы также хотите рассмотреть, действительно ли это хорошая идея - взять на себя ответственность за объект, переданный вам по указателю (и присвоить элементу-указателю, который имеет удаление в деструкторе , является a - не очень очевидно - способ передачи права собственности). Это усложняет управление временем жизни объекта, потому что вы должны помнить, передали ли вы определенный объект объекту, требующему владения (это не проблема, если у вас строгая политика всегда , передающая объектам, требующим владения, хоть). Как обычно, умные указатели могут помочь здесь; однако вы можете подумать, лучше ли сделать копию переданного объекта (для std::string это определенно так, но в любом случае здесь лучше иметь прямой член, как упоминалось выше).

Итак, вот полный список правил, где более ранние правила имеют приоритет перед более поздними, если нет веских причин не использовать их:

  1. Не использовать динамическое распределение.
  2. Управляйте своим динамическим размещением с помощью умных указателей.
  3. Используйте new только в конструкторах и delete только в соответствующем деструкторе.
  4. Всегда имейте new и delete для определенного указателя в функциях-членах одного и того же класса. (На самом деле предыдущее правило является частным случаем этого, но частным случаем, который следует отдавать предпочтению общему.)
1 голос
/ 12 февраля 2012

Если вы используете std::string для ace и ui->label, вам не нужно беспокоиться о том, чтобы память foo->ace была delete d, как только объект foo выходит из области видимости.

Копия правого аргумента доступна для ui->label в = (операция присваивания).Подробнее об этом можно прочитать на справочной странице C ++ std::string для string::operator=.

. Кроме того, таких проблем можно полностью избежать с помощью интеллектуальных указателей, таких как предоставленные библиотека повышения .Прочитайте этот замечательный пост о стекопереработке на эту тему, чтобы лучше понять.

1 голос
/ 12 февраля 2012

Вы должны прочитать о шаблоне RAII . Это одна из самых важных концепций, которые нужно знать программисту на C ++.

Основная идея заключается в том, что время жизни ресурса (новый объект, соединение HTTP и т. Д.) Привязано к времени жизни объекта. Это необходимо для написания безопасного кода исключения.

В вашем случае виджет пользовательского интерфейса сделал бы копию объекта и освободил бы его в своем собственном деструкторе. Затем вызывающий код может сразу освободить свою копию (в другом деструкторе).

0 голосов
/ 12 февраля 2012

Вот более идиоматическая программа на C ++:

class Foo {
public:
    std::string ace;

    Foo() : ace() {
      // nothing to do here. ace knows how to create itself…
    }

    // and copy itself…
    Foo(const Foo& other) : ace(other.ace) {}

     // and clean up after itself…
    ~Foo() {
    }

    // and copy/assign itself…
    Foo& operator=(const Foo& other) {
      this->ace = other.ace;
      return *this;
    }
};


void UI::ButtonPressed() {
  // `new` is not needed here, either!
  Foo foo;
  ui->label = foo.ace; //Set some text on the GUI
  // `delete` is not needed here
}

Если вам действительно нужно вызвать new, всегда используйте соответствующий умный указатель - запись delete изгоняется изсовременный C ++;)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...