class C
{
public:
virtual ~C() {}
virtual void switch_me() = 0;
};
class C1 : public C
{
public:
C1() : b(true) { std::cout << "C1\n"; }
~C1() { std::cout << "~C1\n"; }
private:
void switch_me();
bool b;
};
class C2 : public C
{
public:
C2() : i(1) { std::cout << "C2\n"; }
~C2() { std::cout << "~C2\n"; }
private:
void switch_me();
int i;
};
void C1::switch_me()
{
this->~C1(); // lifetime of *this ends here
std::cout << "blih\n"; // execute some code that does
// not attempt to access object
new(this) C2(); // create a C2 instance in-place
}
void C2::switch_me()
{
this->~C2(); // lifetime of *this ends here
std::cout << "blah\n"; // execute some code...
new(this) C1(); // create a C1 instance in-place
}
class Cnt
{
public:
Cnt() { new(&storage) C1(); }
~Cnt() { (*this)->~C(); }
C* operator->() { return reinterpret_cast<C*>(&storage); }
private:
char storage[std::max(sizeof(C1),sizeof(C2))];
};
int main()
{
Cnt c;
c->switch_me();
c->switch_me();
return 0;
}
答案 0 :(得分:3)
您没有关于switch_me
函数的未定义行为:您在销毁后不以任何方式访问对象,并且在新对象上发生下一次访问。如果保存指针并引用C
对象返回vy operator->
,则可能有UB,并在每次 3.8 / 7 调用switch_me
后使用它:
如果在对象的生命周期结束之后并且在重用或释放对象占用的存储之前,则在原始对象占用的存储位置创建新对象,指向原始对象的指针,引用原始对象的引用,或者原始对象的名称将自动引用新对象,并且一旦新对象的生命周期开始,就可以用于操作新对象,如果:
- 新对象的存储空间正好覆盖存储 原始对象占用的位置,
- 新对象 与原始对象的类型相同(忽略顶级 cv-qualifiers)和
- 原始对象的类型不是 const-qualified,如果是类类型,则不包含任何类型 类型为const限定的非静态数据成员或引用 类型和
- 原始对象是一个派生程度最高的对象(1.8) 类型T,新对象是类型为T的最派生对象(即 是的,它们不是基类子对象。)
你在其他地方有UB,即你的存储空间。它的对齐比您想要放置的对象弱,这可能会导致对齐问题。使用alignas
关键字指定所需的对齐方式:
alignas(C1) alignas(C2) char storage[std::max(sizeof(C1),sizeof(C2))];
如果两个对齐说明符应用于同一声明,则使用更大。