При назначении в C ++ разрушается ли объект, который мы присваиваем? - PullRequest
4 голосов
/ 03 декабря 2010

Утечка следующего фрагмента кода? Если нет, то где разрушаются два объекта, которые созданы в foobar ()?

class B
{
   int* mpI;

public:
   B() { mpI = new int; }
   ~B() { delete mpI; }
};

void foobar()
{
   B b;

   b = B();  // causes construction
   b = B();  // causes construction
}

Ответы [ 5 ]

7 голосов
/ 03 декабря 2010

Оператор присвоения копии по умолчанию выполняет пошаговое копирование.

Так в вашем случае:

{
  B b;      // default construction.
  b = B();  // temporary is default-contructed, allocating again
            // copy-assignment copies b.mpI = temp.mpI
            // b's original pointer is lost, memory is leaked.
            // temporary is destroyed, calling dtor on temp, which also frees
            // b's pointer, since they both pointed to the same place.

  // b now has an invalid pointer.

  b = B();  // same process as above

  // at end of scope, b's dtor is called on a deleted pointer, chaos ensues.
}

Подробнее см. Пункт 11 в Действующем C ++, 2-е издание.

4 голосов
/ 03 декабря 2010

Да, это действительно утечка.Компилятор автоматически предоставляет дополнительный метод, потому что вы его не определили.Генерируемый код эквивалентен следующему:

B & B::operator=(const B & other)
{
    mpI = other.mpI;
    return *this;
}

Это означает, что происходит следующее:

B b; // b.mpI = heap_object_1

B temp1; // temporary object, temp1.mpI = heap_object_2

b = temp1; // b.mpI = temp1.mpI = heap_object_2;  heap_object_1 is leaked;

~temp1(); // delete heap_object_2; b.mpI = temp1.mpI = invalid heap pointer!

B temp2; // temporary object, temp1.mpI = heap_object_3

b = temp1; // b.mpI = temp2.mpI = heap_object_3; 

~temp1(); // delete heap_object_3; b.mpI = temp2.mpI = invalid heap pointer!

~b(); // delete b.mpI; but b.mpI is invalid, UNDEFINED BEHAVIOR!

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

Вместо этого выполните следующее:

class B
{
   int* mpI;

public:
   B() { mpI = new int; }
   B(const B & other){ mpI = new int; *mpi = *(other.mpI); }
   ~B() { delete mpI; }
   B & operator=(const B & other) { *mpI = *(other.mpI); return *this; }
};

void foobar()
{
   B b;

   b = B();  // causes construction
   b = B();  // causes construction
}
3 голосов
/ 03 декабря 2010

Вы строите три объекта, и все будут разрушены. Проблема в том, что по умолчанию оператор копирования-назначения выполняет поверхностную копию. Это означает, что указатель копируется, что приводит к его удалению более одного раза. Это вызывает неопределенное поведение.

Это причина правила 3 . У вас есть деструктор, но нет двух других. Вам нужно реализовать конструктор копирования и оператор присваивания копии, оба из которых должны делать глубокое копирование. Это означает выделение нового типа int, копирование значения поверх.

B(const B& other) : mpI(new int(*other.mpI)) {
}

B& operator = (const B &other) {
    if (this != &other)
    {
        int *temp = new int(*other.mpI);
        delete mpI;
        mpI = temp;
    }
    return *this;
}
0 голосов
/ 03 декабря 2010

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

class B
{
   int* mpI;

public:
   B() {}
   B(int* p) { mpI = p; }
   ~B() {}
};

void foobar()
{
   int* pI = new int;
   int* pJ = new int;

   B b;        // causes construction

   b = B(pI);  // causes construction
   b = B(pJ);  // causes construction

   delete pI;
   delete pJ;
}
0 голосов
/ 03 декабря 2010

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

...