析构函数是否删除了正确的实例[重复]

问题描述 投票:0回答:1

我在带有最新更新的 Visual Studio 2022 社区版中使用 Visual C++。在下面的 C++ 代码中,main 创建了一个 ShowBug 实例并将其分配给变量 sb。下一行创建 ShowBug 的第二个实例并将其分配给 sb.

在第二行完成之前,它调用了析构函数。但是,它并没有破坏第一个实例,这是我认为它会做的,而是破坏了新创建的第二个实例。如果您觉得难以置信,请亲自尝试一下。

所以,我在这里遗漏了什么(即使用相同的变量来分配一个新实例是一种糟糕的编程习惯吗?),或者编译器是否以某种方式做了正确的事情?或者,这是一个编译器错误吗?

// ShowBug.h:

using namespace std;
#include <iostream>
#include <string>

class ShowBug
{
   // This class is used to show that the destructor seems to be called for the wrong instance.
   //
   string S;
   int *pArray;
public:
   inline ShowBug(const string &s) : S(s)
   {
   }

   inline ~ShowBug()
   {
      std::cout << "Deleting " + S;
   }
};

int main()
{
   ShowBug sb = ShowBug("First Instance");
   sb = ShowBug("Second Instance");
}
c++ visual-c++ destructor
1个回答
3
投票

当你做

sb = ShowBug("Second Instance");

表达式

ShowBug("Second Instance")
创建一个 ShowBug 类型的
temporary
对象。一旦完整的表达式(赋值)完成,这个临时对象将立即被销毁。

这当然会导致问题,因为它的销毁也会释放分配的内存。指针将通过赋值进行复制,但指针本身而不是它指向的内存。

所以在赋值之后指针

sb.pArray
将不再有效。除了使用错误的
delete
运算符外,再次尝试
delete
无效指针会导致 undefined behavior.

解决方案是遵循三、五或零的规则之一。我推荐零规则,通过使用

std::vector<int>
或者在你的情况下
std::array<int, 3>
而不是你现在做的动态分配。然后你可以删除析构函数。

如果你必须使用指针和动态分配,那么你至少需要遵循三的规则,并实现一个复制构造函数和一个复制赋值运算符。


我也忽略了初始化

ShowBug sb = ShowBug("First Instance");

有完全相同的问题:您创建了一个临时对象,用于复制构造

sb
对象。然后临时对象被破坏,带走内存和原始指针。

解决方案仍然相同:零、三或五的规则。

© www.soinside.com 2019 - 2024. All rights reserved.