根据C++03 12.4/12,当显式调用析构函数时
如果对象不是析构函数的类类型,也不是从析构函数类类型派生的类,则程序具有未定义的行为
所以我有这个代码:
class Base {};
class Derived : public Base {};
char memory[100];
new(memory) Derived();
Base* ptr = (Base*)memory;
ptr->~Base();
这里的对象类型是Derived
,"析构函数的类类型"是Base
,因此根据标准措辞,看起来没有UB的依据。
那么,上面的代码是否根据标准生成UB?
正确,没有未定义的行为。
相比之下,根据涉及的类型,在这种情况下有潜在的UB:
Base *ptr = new Derived();
delete ptr;
原因是对于某些类型,实现可能已经应用了从Derived*
到Base*
的调整。因此,如果没有指向完整对象的指针,就无法正确释放内存分配。虚拟析构函数确保Base
子对象为实现提供足够的信息来恢复(虚拟调用机制必须能够恢复Derived*
指针,才能将其作为this
传递)。
但在你的例子中,内存并没有被释放,所以没有动机让它成为UB。当然,这仍然是一个坏主意,因为从概念上讲,Derived
对象处于损坏状态。您甚至没有合法的方式来调用~Derived
。在您的示例中,尽管这两种类型都是可破坏的,所以不需要来调用其中任何一种的析构函数。
它不是UB,因为这两个类都有平凡的析构函数,因此调用析构函数的效果与not调用析构因子的效果相同(no当然不是UB)。
如果我错了,请纠正我,我认为没有未定义的行为,但从人性(或可维护性)的角度来看,仍然应该避免。但是,考虑一下Derived
正在创建某种类型的成员,例如共享指针(即使在例外情况下,这也不是不典型的:)。我在我的机器上和代码板上都试过这个代码:
class Base {
public:
boost::shared_ptr<int> x;
};
class Derived : public Base {
public:
boost::shared_ptr<int> y;
};
int main(int argc, char *argv[]) {
boost::shared_ptr<int> xx(new int(0xff));
boost::shared_ptr<int> yy(new int(0xaa));
int memory[100];
for(int i=0; i<100; i++)
memory[i] = 0;
Derived* foo = new(memory) Derived();
foo->x = xx;
foo->y = yy;
(*foo->y)--;
Base* ptr = (Base*)foo;
ptr->~Base();
Derived* bar = new(memory) Derived();
bar->x = xx;
bar->y = yy;
foo->~Derived();
return 0;
}
这里是shared_ptryy没有发布,没有人可以保证永远不会忘记Derived不应该提供任何类型的东西。