C++ 是否允许成员函数显式调用其类析构函数 以下代码是否定义了行为 如果不是,代码的哪一部分是UB,标准的哪一节说明它是UB 如果此代码是UB,是否有任何[微小]更改可以修复它 如果没有任何东西可以修复它,那么可以使用什么其他代码方案/模式来实现相同的功能 C类 { 公众: 虚拟~C(){} 虚拟无效开关_me()=0; }; C1类:公共C类 { 公众: C1():b(真){std::cout switch_me(); 返回0; }

C++ 是否允许成员函数显式调用其类析构函数 以下代码是否定义了行为 如果不是,代码的哪一部分是UB,标准的哪一节说明它是UB 如果此代码是UB,是否有任何[微小]更改可以修复它 如果没有任何东西可以修复它,那么可以使用什么其他代码方案/模式来实现相同的功能 C类 { 公众: 虚拟~C(){} 虚拟无效开关_me()=0; }; C1类:公共C类 { 公众: C1():b(真){std::cout switch_me(); 返回0; },c++,undefined-behavior,self-destruction,C++,Undefined Behavior,Self Destruction,关于切换我函数,您没有未定义的行为:您在销毁后不会以任何方式访问对象,下一次访问发生在新对象上。如果您保存指向C对象的指针和引用,并在调用切换我后按使用它,您可能会有UB特朗>3.8/7: 如果在对象的生命周期结束后,在重用或释放对象占用的存储之前,在原始对象占用的存储位置创建了新对象,则指向原始对象的指针、引用原始对象的引用或原始对象的名称将自动执行引用新对象,并且在新对象的生命周期开始后,可用于操纵新对象,如果: 新对象的存储正好覆盖了存储 原始对象占用的位置,以及 新对象 与原始对象的类

关于
切换我
函数,您没有未定义的行为:您在销毁后不会以任何方式访问对象,下一次访问发生在新对象上。如果您保存指向
C
对象的指针和引用,并在调用
切换我
后按使用它,您可能会有UB特朗>3.8/7:

如果在对象的生命周期结束后,在重用或释放对象占用的存储之前,在原始对象占用的存储位置创建了新对象,则指向原始对象的指针、引用原始对象的引用或原始对象的名称将自动执行引用新对象,并且在新对象的生命周期开始后,可用于操纵新对象,如果:

  • 新对象的存储正好覆盖了存储 原始对象占用的位置,以及
  • 新对象 与原始对象的类型相同(忽略顶层 cv限定符),以及
  • 原始对象的类型不正确 const限定,如果是类类型,则不包含任何 类型为常量限定或引用的非静态数据成员 类型,以及
  • 原始对象是最派生的对象(1.8) 类型T,而新对象是类型T的最派生对象(即 即,它们不是基类子对象)
您在其他位置,即您的存储中确实有UB。它的对齐方式比您要放置在其中的对象弱,这可能会导致对齐问题。使用
alignas
关键字指定所需的对齐方式:

alignas(C1) alignas(C2) char storage[std::max(sizeof(C1),sizeof(C2))];

如果对同一声明应用了两个对齐说明符,则使用较大的值。

开关\u me的代码为****@πάνταῥεῖ: 绝对不是重复!在我的问题中,成员函数调用自己的类析构函数(即:它是自毁函数)。在销毁和构造之间,您不会以任何方式调用任何成员函数或访问对象,因此虚拟成员函数与您的问题无关。但是,您可能会遇到对齐问题:
存储
完全不对齐。@shrike如果没有任何东西可以修复它,那么可以使用什么其他代码方案/模式来实现它同样的特性——而不是张贴晦涩的C++代码,你真正想要解决的问题是什么?@保尔麦肯齐:什么是模糊的?这是一种很明显的重新访问的状态模式,我想知道这个实现是否是UB或……谢谢你的宝贵答案。在以前的设计中,我在代码< CNT< /代码>中定义了<代码>操作符*()。返回对存储的引用;正如您在回答中所注意到的,这是UB,我想使用
operator->()
确保新设计不是。关于对齐,在实际代码中,我使用
std::aligned\u存储
而不是
char[]
,所以是的,在我的代码片段中是UB,但在真实的代码中不是。如果没有其他人在某处注意到其他UB,我可能会接受你的答案……无论如何,谢谢。
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;
}
alignas(C1) alignas(C2) char storage[std::max(sizeof(C1),sizeof(C2))];