C ++: Разница между использованием нового ключевого слова и нет при создании экземпляров членов класса? - PullRequest
2 голосов
/ 13 февраля 2011

Для программного задания нам дается шаблонный класс с двумя членами, объявленными не как указатели, а как реальные объекты:

Foo member;

В конструкторе я сначала попытался member = *(new Foo());, но узнал, что,по крайней мере, иногда это копировало новый объект Foo и, следовательно, вызывало утечки памяти.

Я наконец обнаружил member = Foo(), а затем посмотрел, в чем разница.Я узнал, что член будет размещен в стеке, а не в куче, и что он будет удален, как только выйдет из области видимости.Как это работает для объектов?

Член удаляется только при удалении родительского / класса объекта?

У меня также есть другой вопрос по поводу member = *(new Foo());.Я инициализировал две переменные-члены одного типа:

// Members
Foo member1;
Foo member2;

// Constructor {
    member1 = *(new Foo());
    member2 = *(new Foo());
}

По какой-то причине казалось, что member1 не копируется, и он сохранил тот же адрес, что и исходный Foo (т.е. не было памятиутечка когда была удалена).member2 однако, будет скопирован и имел другой адрес, и память просочилась.Есть ли объяснение этому?

Ответы [ 2 ]

4 голосов
/ 13 февраля 2011

Ваш анализ неверен. Оба из них утечки памяти. Что вы делаете, это выделяете новую копию объекта, присваиваете значение члену, а затем отбрасываете указатель на выделенную память без освобождения этой памяти. Это утечка памяти в обоих случаях.

Теперь рассмотрим следующий код:

class MemberType {
  public:
    MemberType() { std::cout << "Default constructor" << std::endl; }
    MemberType(int) { std::cout << "Int constructor" << std::endl; }
};

class Example1 {
  public:
     Example1() {}
  private:
     MemberType member_;
};

class Example2 {
  public:
      Example2() : member_(5) {}
  private:
      MemberType member_;
};

int main(int argc, char** argv) {
   Example1 example1;
   Example2 example2;
   return 0;
}

Этот код будет печатать оба разных типа конструкторов. Обратите внимание, что для инициализации элемента по умолчанию не потребовался какой-либо код инициализации. Следовательно, ваш новый оператор (или даже присвоение без нового) был бы не нужен в случае инициализации по умолчанию. При инициализации членов с использованием конструктора, отличного от конструктора по умолчанию, правильный способ сделать это с помощью списка инициализатора. (Список инициализатора - это то, что происходит с ": member_ (5)" в примере.

Пожалуйста, смотрите C ++ FAQ по конструкторам для получения дополнительной информации о построении объектов в C ++.

2 голосов
/ 13 февраля 2011
member = *(new Foo());

new Foo() динамически распределяет объект Foo и возвращает указатель на этот объект. * разыменовывает этот указатель, давая вам объект Foo. Этот объект затем присваивается member, что включает вызов оператора присвоения копии Foo. По умолчанию этот оператор присваивает значения каждому из членов правого объекта (*(new Foo())) в объекте левой стороны (member).

Проблема заключается в следующем: new Foo() динамически распределяет объект Foo, и этот объект не уничтожается, пока вы не delete указатель, возвращенный из new. Вы нигде не сохраняете этот указатель, поэтому вы пропустили динамически размещенный объект.

Это неправильный способ инициализации объекта.

member = Foo();

При этом создается временный инициализированный объект Foo, и этот объект назначается member. В конце этого оператора (по факту ;) временный объект уничтожается. member не уничтожается, и содержимое временного Foo объекта было скопировано в member, так что это именно то, что вы хотите сделать.

Обратите внимание, что предпочтительным способом инициализации переменных-членов является использование списка инициализаторов:

struct C {
    Foo member1, member2;

    C() : member1(), member2() { }
};
...