C++ 在对象被显式销毁后但在其内存被释放之前调用成员函数是否合法?
我有以下代码:C++ 在对象被显式销毁后但在其内存被释放之前调用成员函数是否合法?,c++,destructor,language-lawyer,object-lifetime,explicit-destructor-call,C++,Destructor,Language Lawyer,Object Lifetime,Explicit Destructor Call,我有以下代码: struct data { void doNothing() {} }; int main() { data* ptr = new data(); ptr->~data(); ptr->doNothing(); ::operator delete(ptr); } 请注意,doNothing()是在对象被销毁后但其内存被释放之前调用的。看起来“对象生存期”已经结束,但是指针仍然指向正确分配的内存。成员函数不访问任何成员变量 在这种
struct data {
void doNothing() {}
};
int main() {
data* ptr = new data();
ptr->~data();
ptr->doNothing();
::operator delete(ptr);
}
请注意,doNothing()
是在对象被销毁后但其内存被释放之前调用的。看起来“对象生存期”已经结束,但是指针仍然指向正确分配的内存。成员函数不访问任何成员变量
在这种情况下,成员函数调用合法吗?是的,在OP中的代码中。因为析构函数很简单,调用它不会结束对象的生命周期。[基本生活]/p1: 类型为
T
的对象的生存期在以下情况下结束:
- 如果
是具有非平凡析构函数(12.4)的类类型,则析构函数调用将启动,或者T
- 对象占用的存储被重用或释放
- 析构函数不是虚拟的
- 它的类的所有直接基类都有平凡的析构函数,并且
- 对于其类中属于类类型(或其数组)的所有非静态数据成员,每个此类类都有一个简单的 析构函数
void*
,定义良好。通过这样一个指针进行间接寻址是非常困难的
允许,但由此产生的左值只能以有限的方式使用,
如下所述。程序未定义
以下情况下的行为:
- [……]
- 指针用于访问非静态数据成员或调用对象的非静态成员函数,或
- [……]
-…
-指针用于访问非静态数据成员或调用 反对 规定您拥有的是未定义的行为。然而,这里有不同的语言——“对象不再存在”和“对象已经结束”,在[basic.life]的前面,它说: 其初始化已完成。 类型为
T
的对象的生存期在以下时间结束:-如果
T
是具有非平凡析构函数(12.4)的类类型,则析构函数调用将启动,或者-对象占用的存储被重用或释放 一方面,您没有一个非平凡的析构函数,因此[basic.life]表明对象的生存期尚未结束-存储尚未被重用或释放。另一方面,[class.dtor]表明对象“不再存在”,这听起来肯定像是“结束”的同义词,但实际上并非如此
我想“语言律师”的答案是:从技术上讲,这不是未定义的行为,似乎完全合法。“代码质量”的答案是:不要这样做,这充其量是令人困惑的 其他答案都是正确的,但省略了一个细节: 如果析构函数或构造函数都是平凡的,则允许使用。其他答案清楚地解释了,如果析构函数是微不足道的,那么原始对象的生命周期并没有结束 但是,如果构造函数是平凡的,那么只要存在适当大小和对齐方式的内存位置,对象就会存在。因此,即使有一个非平凡的析构函数和平凡的构造函数,也存在一个全新的对象,您可以调用其成员 其他答案遗漏的措辞,紧接着在他们引用的生命终结规则之前,说 对象的生存期是对象的运行时属性。如果一个对象属于类或聚合类型,并且它或它的一个成员是由一个构造函数(而不是一个普通的默认构造函数)初始化的,则称该对象具有非真空初始化。[注:由普通复制/移动构造函数进行的初始化是非真空初始化。-结束注]类型为
T
的对象的生存期始于:
- 获得类型
的正确对齐和尺寸的存储,以及T
- 如果对象具有非真空初始化,则其初始化已完成
在OP的情况下,原始对象仍然存在,因此此警告不适用。无论答案如何,这都会让以后试图维护代码的人感到非常困惑。不要这样做,即使它以某种方式被证明是合法的(或在某个场景中工作)我认为这是不合法的,但是我读C++规范已经有一段时间了,我可能是错的。