析构函数是否删除了正确的实例

Bob*_*yan 0 c++ destructor visual-c++

我在具有最新更新的 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");
}
Run Code Online (Sandbox Code Playgroud)

Som*_*ude 5

当你这样做时

sb = ShowBug("Second Instance");
Run Code Online (Sandbox Code Playgroud)

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

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

所以赋值后指针sb.pArray将不再有效。除了使用错误的delete运算符之外,再次尝试delete无效指针也会导致未定义的行为

解决方案是遵循三、五或零规则之一。我建议使用零规则,std::vector<int>在您的情况下使用 orstd::array<int, 3>而不是您现在所做的动态分配。然后你可以删除析构函数。

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


我还忽略了初始化

ShowBug sb = ShowBug("First Instance");
Run Code Online (Sandbox Code Playgroud)

有完全相同的问题:您创建一个临时对象,用于复制构造该sb对象。然后临时对象被销毁,并带走内存和原始指针。

但解决方案仍然是一样的:零、三或五的规则。

  • `ShowBug sb = ShowBug("First Instance");` 仅创建一个对象;`sb` 由构造函数直接初始化。请参阅http://eel.is/c++draft/dcl.init#general-16.6.1 (2认同)