堆栈对象"删除"的行为是什么?

sk *_*tra 5 c++ memory stack memory-management

int main()
{
    Class_Name t;
    Class_Name * p = &t;

    delete p;
    return 0;
}  
Run Code Online (Sandbox Code Playgroud)

这个代码执行正常,调用2个析构函数?delete如何使用堆栈对象?行为是否未定义?

use*_*027 12

您遇到了未定义的行为.

标准(N3690)5.3.5 [expr.delete]/ 2

如果操作数具有类类型,则通过调用上述转换函数将操作数转换为指针类型,并使用转换后的操作数代替本节其余部分的原始操作数.在第一个替代(删除对象)中,delete的操作数的值可以是空指针值,指向前一个新表达式创建的非数组对象的指针,或指向表示一个子对象的子对象(1.8)的指针.这种对象的基类(第10条).如果不是,则行为未定义.
...

您没有空指针,也没有先前使用new分配的Object,因此行为未定义.

注意:即使在尝试时也是如此

int main()
{
    Class_Name t;
    t.~Class_Name()
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

这将是未定义的行为.即使它没有删除,只是因为它显式调用具有自动存储持续时间的Object的析构函数.这意味着析构函数将被调用两次,一次是在显式调用时,第二次是在离开它的范围时.

标准12.4 [class.dtor]/ 15

一旦为对象调用析构函数,该对象就不再存在; 如果为生命周期结束的对象调用析构函数,则行为未定义(3.8).[ 示例:如果显式调用自动对象的析构函数,并且该块随后以通常调用对象的隐式销毁的方式保留,则行为未定义.- 末端的例子 ]

大多数时候试图做这样的事情(希望)会导致崩溃.通过一个简单的解构函数,你可能会有(坏)运气,但没有任何反应.

这里的术语很少:C++标准没有谈论堆栈与堆对象,它总是分别讨论自动存储持续时间和动态存储持续时间.正如您在上面的引用中也可以看到的那样.


您应该始终遵循一般准则:

  • 对于堆栈分配的对象,不进行任何显式释放/删除(自动调用析构函数).
  • 每个new应该有一个对应的delete
  • 每个new[]应该有一个对应的delete[]
  • 每个malloccalloc应该有一个相应的free

  • @BЈовић它明确地说只有空指针和指向"new"分配对象的指针是有效的,其他一切都是无效的.因此,对于删除表达式,指向堆栈上的对象的指针是无效的. (6认同)
  • @Tribse:但是应该补充一点,要使subobject/base子句工作,基础必须有虚拟析构函数.它是规范中的一个不同的子句,要求它,但崩溃实际上来自删除,它将无法找到对象实际开始的位置. (2认同)