当变量仍然存在时调用 C++ 析构函数

Posted

技术标签:

【中文标题】当变量仍然存在时调用 C++ 析构函数【英文标题】:C++ Destructor being called when variable still alive 【发布时间】:2018-03-22 09:04:54 【问题描述】:

我有一个带有指向整数的指针的类。

然后是一个静态函数,它将返回该整数的值。

我注意到在调用静态函数时,每次都会为该对象调用析构函数。

我不明白为什么会发生这种行为。

class Dog

public:

Dog(int val)
 this->pVal = new int(val);


~Dog()
 delete this->pVal;


static int GetVal(Dog d)
  return *(d.pVal);


int *pVal;
;

这就是类。

这是我的测试驱动程序代码。

Dog fido(20);
std::cout << Dog::GetVal(fido);  //20 and destructor for fido called
Dog rex(21);
std::cout << Dog::GetVal(fido); //21 but should be 20 
std::cout << Dog::GetVal(rex);   // should be 21

我注意到两个 dog 对象位于不同的内存地址,但 int 指针位于同一地址。我相信这是因为调用 GetVal 时调用了 fido 的析构函数,但我不知道为什么会发生这种行为。

【问题讨论】:

如果您使用 unique_ptr 而不是手动管理生命周期,您的编译器会显示您的错误(代码无法编译)。 仅仅因为另一个问题的答案适用于这个问题并不会使这个问题重复。这个问题是通过证明一个问题提出的; “重复”询问特定主题。除非你已经知道这个问题的代码有什么问题,否则你不会知道所谓的重复是相关的。 我同意 Darryl 的上述评论。投票重新开放。 【参考方案1】:

虽然确实调用了“Fido”的析构函数,但这不是原来的“Fido”,而是它的副本。您的GetVal(Dog d) 函数按值获取Dog,这意味着“Fido”在传递给GetVal 之前被复制,然后在完成时副本被销毁。

通过const 引用传递Dog&amp; 可解决此问题:

static int GetVal(const Dog& d)
    return *(d.pVal);

Demo.

注意:上面没有解释为什么你会因为“Fido”而得到 21。由于您没有定义复制构造函数或赋值运算符,编译器为您生成了一个简单的复制构造函数。结果,“Fido”副本中的pVal 指向与原始“Fido”中的pVal 相同的位置。一旦从Dog::GetVal(fido) 返回时副本被销毁,内存就可以重用,并且原始“Fido”内的指针变得悬空。当您第二次调用 Dog::GetVal(fido) 时,该函数会通过取消引用悬空指针来导致未定义的行为。第二次调用打印 21 的事实,即您传递给“Rex”构造函数的值,强烈表明在销毁“Fido”副本时释放的内存正在立即重用于构造“Rex”。但是,C++ 不需要这样做。如果发生这种情况,当“Rex”和“Fido”在运行结束时被销毁时,您的代码会第二次导致 UB。

【讨论】:

啊,非常感谢。我没有意识到已创建副本。 @mocode10 您应该遵循一些规则来防止出现此类问题。具体来说,五规则或零规则:github.com/isocpp/CppCoreGuidelines/blob/master/…、github.com/isocpp/CppCoreGuidelines/blob/master/… 解决这个问题的另一种方法是重载复制构造函数以创建一个新的 int 指针。

以上是关于当变量仍然存在时调用 C++ 析构函数的主要内容,如果未能解决你的问题,请参考以下文章

C++入门(拷贝)构造函数和析构函数

c++中构造和析构函数

C++构造和析构

c++ 复制构造函数和析构函数

信号处理以确保在 C++ 中调用析构函数

C++ - 构造函数与析构函数的使用