显式调用析构函数

And*_*cco 21 c++ destructor lifetime undefined-behavior explicit-destructor-call

我偶然发现了以下代码片段:

#include <iostream>
#include <string>
using namespace std;
class First
{
    string *s;
    public:
    First() { s = new string("Text");}
    ~First() { delete s;}
    void Print(){ cout<<*s;}
};

int main()
{
    First FirstObject;
    FirstObject.Print();
    FirstObject.~First();
}
Run Code Online (Sandbox Code Playgroud)

该文本表示此代码段应该导致运行时错误.现在,我对此并不十分肯定,所以我尝试编译并运行它.有效.奇怪的是,尽管所涉及的数据非常简单,但在打印"文本"之后程序结结巴巴,并且仅在一秒钟之后完成.

我添加了一个要打印到析构函数的字符串,因为我不确定显式调用这样的析构函数是否合法.程序打印两次字符串.所以我的猜测是析构函数被调用两次,因为正常的程序终止不知道显式调用并试图再次销毁对象.

一个简单的搜索确认显式调用自动化对象上的析构函数是危险的,因为第二次调用(当对象超出范围时)具有未定义的行为.所以我很幸运,我的编译器(VS 2017)或这个特定的程序.

关于运行时错误,文本是否完全错误?或者运行时错误真的很常见吗?或者也许我的编译器实现了某种针对这类事情的warding机制?

YSC*_*YSC 34

一个简单的搜索确认显式调用自动化对象上的析构函数是危险的,因为第二次调用(当对象超出范围时)具有未定义的行为.

那是真实的.如果使用自动存储显式销毁对象,则会调用未定义的行为.了解更多相关信息.

所以我很幸运,我的编译器(VS 2017)或这个特定的程序.

我说你不走运.UB可能发生的最好(对你而言,编码器)是第一次运行时的崩溃.如果它似乎工作正常,崩溃可能发生在2038年1月19日的生产中.

关于运行时错误,文本是否完全错误?或者运行时错误真的很常见吗?或者也许我的编译器实现了某种针对这类事情的warding机制?

是的,文字有点不对劲.未定义的行为未定义.运行时错误只是众多可能性中的一种(包括鼻子恶魔).

关于未定义行为的好读物:什么是未定义的行为?

  • "*当您使用自动存储*显式销毁对象时,将调用未定义的行为" - 更像是在对象到达其自然生命周期结束时调用它而不会被重新生成.析构函数调用本身不是UB本身. (5认同)
  • 更准确地说,2038年1月19日03:14:08 UTC:D (3认同)

Sha*_*our 15

不,这只是草案C++标准[class.dtor] p16中未定义的行为:

一旦为对象调用析构函数,该对象就不再存在; 如果为生命周期结束的对象([basic.life])调用析构函数,则行为未定义.[实施例:如果用于自动对象的析构函数被显式调用,并且,通常会调用该对象的隐式的破坏的方式将块随后离开,该行为是未定义. - 结束的例子  

我们可以从未定义行为定义中看出:

本文档没有要求的行为

你对结果没有任何期望.对于特定编译器上的作者而言,它可能在特定机器上具有特定选项,但我们不能指望它是可移植的,也不是可靠的结果.虽然有些情况下实现确实尝试获取特定结果,但这只是可接受的未定义行为的另一种形式.

另外[class.dtor] p15给出了上面引用的规范部分的更多背景:

[注意:很少需要显式调用析构函数.此类调用的一个用途是使用放置new-expression放置在特定地址的对象.为了处理专用硬件资源和编写存储器管理设施,可能需要使用显式放置和销毁对象.例如,

void* operator new(std::size_t, void* p) { return p; }
struct X {
  X(int);
  ~X();
};
void f(X* p);

void g() {                      // rare, specialized use:
  char* buf = new char[sizeof(X)];
  X* p = new(buf) X(222);       // use buf[] and initialize
  f(p);
  p->X::~X();                   // cleanup
}
Run Code Online (Sandbox Code Playgroud)

- 结束说明]


gsa*_*ras 9

关于运行时错误,文本是否完全错误?

这是错误的.

或者运行时错误真的很常见吗?或者也许我的编译器实现了某种针对这类事情的warding机制?

您无法知道,这就是当您的代码调用未定义的行为时会发生的情况; 你不知道执行它会发生什么.

在你的情况下,你是(非)幸运*它工作,而对我来说,它造成了一个错误(双免费).


*因为如果你收到错误就会开始调试,否则,在一个大型项目中,你可能会错过它......