通过在此指针上放置new来重新初始化对象时的未定义行为

Fla*_*ire 7 c++ destructor placement-new language-lawyer

我看到了关于Piotr Padlewski的cppcon的演示文稿,说明以下是未定义的行为:

int test(Base* a){
  int sum = 0;
  sum += a->foo();
  sum += a->foo();
  return sum;
}

int Base::foo(){
  new (this) Derived;
  return 1;
}
Run Code Online (Sandbox Code Playgroud)

注意:假设sizeof(Base) == sizeof(Derived)并且foo是虚拟的.

显然这很糟糕,但我很感兴趣为什么它是UB.我确实理解UB访问realloced指针,但他说,这是相同的.

相关问题:`new(this)MyClass();`直接调用析构函数后的未定义行为?在哪里说"ok,如果没有例外" 是否有效直接调用(虚拟)析构函数?它说的new (this) MyClass();结果是UB.(与上述问题相反)

C++使用放置新的未定义行为构造对象两次?它说:

程序可以通过重用对象占用的存储来结束任何对象的生命周期,或者通过使用非平凡的析构函数显式调用类类型的对象的析构函数来结束任何对象的生命周期.对于具有非平凡析构函数的类类型的对象,程序不需要在重用或释放对象占用的存储之前显式调用析构函数; 但是,如果没有显式调用析构函数或者如果没有使用delete-expression(5.3.5)来释放存储,则不应该隐式调用析构函数,并且任何程序都依赖于析构函数产生的副作用有未定义的行为.

这听起来好像没问题.

我在Placement new中找到了另一个新的贴图描述,并使用const成员分配了类

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

  • 新对象的存储完全覆盖原始对象占用的存储位置,以及

  • 新对象与原始对象的类型相同(忽略顶级cv限定符),和

  • 原始对象的类型不是const限定的,如果是类类型,则不包含任何类型为const限定的非静态数据成员或引用类型,以及

  • 原始对象是类型为T的派生程度最高的对象,新对象是类型为T的派生程度最高的对象(也就是说,它们不是基类子对象).

这似乎解释了UB.但这是真的吗?

这是不是意味着,我不能拥有std::vector<Base>?因为我假设由于它的预分配std::vector必须依赖placement-news和显式ctors.第4点要求它是最衍生的类型,Base显然不是.

Ste*_*ner 2

关于你的问题

...因为我假设由于其预分配 std::vector 必须依赖于放置新闻和显式构造函数。第 4 点要求它是最派生的类型,而 Base 显然不是。第 4 点要求它是最派生的类型,而 Base 显然不是。

,我认为误解来自术语“最派生对象”或“最派生类型”:

类类型对象的“最派生类型”是实例化该对象的类,无论该类是否有进一步的子类。考虑以下程序:

struct A {
    virtual void foo() { cout << "A" << endl; };
};

struct B : public A {
    virtual void foo() { cout << "B" << endl; };
};

struct C : public B {
    virtual void foo() { cout << "C" << endl; };
};

int main() {

    B b;  // b is-a B, but it also is-an A (referred  to as a base object of b).
          // The most derived class of b is, however, B, and not A and not C.
}
Run Code Online (Sandbox Code Playgroud)

当您现在创建 a 时vector<B>,该向量的元素将是 class 的实例B,因此元素的最派生类型将始终是B, 而不是C(或Derived) 在您的情况下。

希望这能带来一些启发。