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机制?
是的,文字有点不对劲.未定义的行为未定义.运行时错误只是众多可能性中的一种(包括鼻子恶魔).
关于未定义行为的好读物:什么是未定义的行为?
Sha*_*our 15
不,这只是草案C++标准[class.dtor] p16中未定义的行为:
一旦为对象调用析构函数,该对象就不再存在; 如果为生命周期结束的对象([basic.life])调用析构函数,则行为未定义.[实施例:如果用于自动对象的析构函数被显式调用,并且,通常会调用该对象的隐式的破坏的方式将块随后离开,该行为是未定义. - 结束的例子
本文档没有要求的行为
你对结果没有任何期望.对于特定编译器上的作者而言,它可能在特定机器上具有特定选项,但我们不能指望它是可移植的,也不是可靠的结果.虽然有些情况下实现确实尝试获取特定结果,但这只是可接受的未定义行为的另一种形式.
另外[class.dtor] p15给出了上面引用的规范部分的更多背景:
[注意:很少需要显式调用析构函数.此类调用的一个用途是使用放置new-expression放置在特定地址的对象.为了处理专用硬件资源和编写存储器管理设施,可能需要使用显式放置和销毁对象.例如,
Run Code Online (Sandbox Code Playgroud)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 }- 结束说明]