在C ++中分配时,我们分配的对象是否会被破坏?

时间:2010-12-03 00:50:08

标签: c++ rule-of-three

以下代码片段是否泄漏?如果没有,那么在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 个答案:

答案 0 :(得分:7)

默认的复制赋值运算符执行成员方复制。

所以在你的情况下:

{
  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.
}

有关详细信息,请参阅Effective C ++第2版中的第11项。

答案 1 :(得分:4)

是的,这确实泄漏了。编译器会自动提供额外的方法,因为您尚未定义它。它生成的代码等同于:

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!

这显然很糟糕。在您违反rule of three的任何情况下都可能发生这种情况。您已经定义了一个非平凡的析构函数和一个复制构造函数。但是,您尚未定义副本分配。三条规则是,如果你定义上述任何一个,你应该总是定义所有三个。

相反,请执行以下操作:

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
}

答案 2 :(得分:3)

你正在建造三个物体,所有物体都将被毁坏。问题是默认的复制赋值运算符将执行浅复制。这意味着指针被复制,这导致它被删除多次。这会导致未定义的行为。

这就是rule of 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;
}

答案 3 :(得分:0)

正如多次指出的那样,你违反了三条规则。只是为了添加链接,对堆栈溢出有一个很好的讨论:What is The Rule of Three?

答案 4 :(得分:0)

只是提供一种不同的方法来解决我最初发布的代码问题,我想我可以将指针保留在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;
}