我在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;
}
注意:假设sizeof(Base) == sizeof(Derived)
和foo
是虚拟的。
显然这很糟糕,但我很感兴趣为什么它是UB。我确实理解UB访问realloc
ed指针,但他说,这是相同的。
相关问题:Is `new (this) MyClass();` undefined behaviour after directly calling the destructor?其中显示“如果没有例外,则确定”
Is it valid to directly call a (virtual) destructor? new (this) MyClass();
表示会导致UB。 (与上述问题相反)
C++ Is constructing object twice using placement new undefined behaviour?它说:
程序可以通过重用存储来结束任何对象的生命周期 对象占用的内容或通过显式调用析构函数 具有非平凡析构函数的类类型的对象。对于一个对象 对于具有非平凡析构函数的类类型,程序不是 需要在存储之前显式调用析构函数 对象占用被重用或释放;但是,如果没有 显式调用析构函数或者如果是delete-expression(5.3.5) 不习惯释放存储,析构函数不得 隐式调用以及任何依赖于副作用的程序 由析构函数生成的行为具有不确定的行为。
再次听起来没问题。
我在Placement new and assignment of class with const member
中找到了新的展示位置的另一个描述如果在对象的生命周期结束之后和存储之前 对象占用的是重用或释放的,一个新的对象是 在原始对象占用的存储位置创建,a 指向原始对象的指针,引用的引用 到原始对象,或原始对象的名称 自动引用新对象,一旦生命周期 新对象已启动,可用于操作新对象,如果:
新对象的存储空间恰好覆盖原始对象占用的存储位置,
新对象与原始对象的类型相同(忽略顶级cv限定符),
原始对象的类型不是const限定的,如果是类类型,则不包含任何类型为的非静态数据成员 const限定或引用类型,
原始对象是类型为T的派生程度最高的对象,而新对象是类型为T的派生程度最高的对象(也就是说,它们不是 基类子对象)。
这似乎解释了UB。但这是真的吗?
这是不是意味着我没有std::vector<Base>
?因为我认为由于其预分配std::vector
必须依赖placement-new
和明确的ctors。第4点要求它是Base
显然不是最派生的类型。
答案 0 :(得分:3)
我相信Elizabeth Barret Browning说得最好。让我数一下。
Base
不是可以轻易破坏的,我们就无法清理资源。 sizeof(Derived)
大于动态类型this
的大小,我们就会破坏其他内存。 Base
不是Derived
的第一个子对象,则新对象的存储空间不会完全覆盖原始存储空间,您也会结束打破其他记忆。Derived
只是与初始动态类型不同的类型,即使它的大小与我们在{{3}上调用foo()
的对象相同引用新对象。如果Base
或Derived
的任何成员具有const
资格或是参考资料,情况也是如此。您需要std::launder
任何外部指针/引用。但是,如果sizeof(Base) == sizeof(Derived)
和Derived
可以轻易破坏,Base
是Derived
的第一个子对象,您实际上只有Derived
个对象。 .. 这可以。
答案 1 :(得分:2)
关于你的问题
...因为我假设由于它的预分配std :: vector必须依赖 安置新闻和明确的ctors。第4点要求它是 最基本的类型哪个Base显然不是。第4点需要它 是基本显然不是最基本的类型。
,我认为误解来自术语“最衍生对象”或“最衍生类型”:
类类型对象的“最派生类型”是实例化对象的类,无论该类是否具有其他子类。请考虑以下程序:
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.
}
现在创建vector<B>
时,此向量的元素将是类B
的实例,因此元素的派生类型总是B
,并且在你的情况下不是C
(或Derived
。
希望这会带来一些启示。