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

发布于 2024-10-06 06:27:07 字数 262 浏览 8 评论 0原文

下面的代码片段是否泄漏?如果不是,那么 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
}

Does the following code fragment leak? If not, where do the two objects which are constructed in foobar() get destructed?

class B
{
   int* mpI;

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

void foobar()
{
   B b;

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

情丝乱 2024-10-13 06:27:07

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

因此,对于您的情况:

{
  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++,第二版》中的第 11 条了解更多详细信息。

The default copy assignment operator does a member-wise copy.

So in your case:

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

See Item 11 in Effective C++, 2nd Edition for more details.

红ご颜醉 2024-10-13 06:27:07

是的,这确实会泄漏。编译器会自动提供一个额外的方法,因为您还没有定义它。它生成的代码与此等效:

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
}

Yes, this does leak. The compiler automatically provides an extra method because you have not defined it. The code it generates is equivalent to this:

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

This means that the following stuff happens:

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!

This is obviously bad. This is likely to occur in any instance that you violate the rule of three. You have defined a non-trivial destructor and also a copy constructor. You have not defined a copy assignment, though. The rule of three is that if you define any of the above, you should always define all three.

Instead, do the following:

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
}
流年里的时光 2024-10-13 06:27:07

您正在构建三个对象,所有对象都将被破坏。问题是默认的复制赋值运算符将执行浅复制。这意味着指针被复制,从而导致它被多次删除。这会导致未定义的行为。

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

You're constructing three objects, and all will be destructed. The problem is that the default copy-assignment operator will do a shallow copy. That means the pointer is copied over, which causes it be be deleted more than once. This causes undefined behavior.

This is the reason behind the rule of 3. You have a destructor but not the other two. You need to implement a copy constructor and copy assignment operator, both of which should do a deep copy. This means allocating a new int, copying the value over.

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;
}
凌乱心跳 2024-10-13 06:27:07

正如多次指出的,你违反了三法则。只是为了添加链接,在堆栈溢出上对此进行了很好的讨论:什么是三法则?

As pointed out several times, you've violated the rule of three. Just to add to the links, there is a great discussion of this on stack overflow: What is The Rule of Three?

做个少女永远怀春 2024-10-13 06:27:07

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

Just to offer a different approach to solving the problems of the code I originally posted, I think I could keep the pointer in class B, but take the memory mangement out. Then I need no custom destructor, and so I don't violate the rule of 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;
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文