#include <cstdlib>
struct B {
virtual void f();
void mutate();
virtual ~B();
};
struct D1 : B { void f(); };
struct D2 : B { void f(); };
void B::mutate() {
new (this) D2; // reuses storage — ends the lifetime of *this
f(); // undefined behavior - WHY????
... = this; // OK, this points to valid memory
}
我需要解释一下原因f()
调用有UB吗?new (this) D2;
重用存储,但它也调用构造函数D2
自此开始新对象的生命周期。在这种情况下f()
等于this -> f()
. 这就是我们刚刚调用的f()
的成员函数D2
.谁知道为什么是UB?
该标准显示了此示例 § 3.8 67 N3690:
struct C {
int i;
void f();
const C& operator=( const C& );
};
const C& C::operator=( const C& other) {
if ( this != &other ) {
this->~C(); // lifetime of *this ends
new (this) C(other); // new object of type C created
f(); // well-defined
}
return *this;
}
C c1;
C c2;
c1 = c2; // well-defined
c1.f(); // well-defined; c1 refers to a new object of type C
请注意,此示例在就地构造新对象之前终止对象的生命周期(与不调用析构函数的代码相比)。
但即使你这样做了,标准也说:
如果在对象的生命周期结束之后且在存储之前
所占用的对象被重用或释放,一个新的对象是
在原始对象占用的存储位置创建,
指向原始对象的指针,引用的引用
到原始对象,或者原始对象的名称将
自动引用新对象,并且一旦该对象的生命周期结束
新对象已经启动,可以用来操作新对象,如果:
— 新对象的存储完全覆盖存储位置
原始对象占据的位置,并且 — 新对象属于
与原始对象相同的类型(忽略顶层
简历限定符),以及
— 原始对象的类型不是
const 限定的,并且如果是类类型,则不包含任何非静态
类型为 const 限定的数据成员或引用类型,以及
— 原始对象是类型 T 的最派生对象 (1.8),并且
new 对象是类型 T 的最派生对象(也就是说,它们不是
基类子对象)。
注意“and”字样,以上条件必须全部满足。
由于您没有满足所有条件(您将派生对象放入基类对象的内存空间中),因此您有未定义的行为当隐式或显式使用此指针引用内容时。
根据编译器的实现,这可能会失败,因为基类虚拟对象为vtable, 就地构造派生类型的对象(覆盖某些虚拟函数)意味着 vtable 可能不同,考虑到对齐问题和其他低级内部结构,您将发现简单的 sizeof 不足以确定您的代码是否正确。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)