通过在此指针上放置new来重新初始化对象的未定义行为 [英] Undefined behaviour on reinitializing object via placement new on this pointer
问题描述
I saw a presentation on cppcon of Piotr Padlewski saying that the following is undefined behaviour:
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
是虚拟的.
Note: Assume sizeof(Base) == sizeof(Derived)
and foo
is virtual.
显然这很糟糕,但是我对为什么它是UB感兴趣.我确实了解UB关于访问realloc
ed指针的信息,但是他说,这是相同的.
Obviously this is bad, but I'm interested in WHY it is UB. I do understand the UB on accessing a realloc
ed pointer but he says, that this is the same.
相关问题:是新的( this)MyClass();`直接调用析构函数后的未定义行为?其中显示没有异常,可以"
直接调用(虚拟)析构函数是否有效?说new (this) MyClass();
会导致UB. (与上述问题相反)
Related questions: Is `new (this) MyClass();` undefined behaviour after directly calling the destructor? where it says "ok if no exceptions"
Is it valid to directly call a (virtual) destructor? Where it says new (this) MyClass();
results in UB. (contrary to the above question)
C ++使用放置新的undefined构造了两次对象行为?它说:
程序可以通过重新使用存储来结束任何对象的生命周期 对象所占用的对象,或通过显式调用析构函数以获取 具有非平凡析构函数的类类型的对象.对于一个对象 具有非平凡析构函数的类类型,则程序不是 需要在存储之前显式调用析构函数 占用的对象被重用或释放;但是,如果没有 显式调用析构函数或delete-expression(5.3.5)是 不用于释放存储,则不应使用析构函数 隐式调用以及任何依赖于副作用的程序 析构函数产生的行为不确定.
A program may end the lifetime of any object by reusing the storage which the object occupies or by explicitly calling the destructor for an object of a class type with a non-trivial destructor. For an object of a class type with a non-trivial destructor, the program is not required to call the destructor explicitly before the storage which the object occupies is reused or released; however, if there is no explicit call to the destructor or if a delete-expression (5.3.5) is not used to release the storage, the destructor shall not be implicitly called and any program that depends on the side effects produced by the destructor has undefined behavior.
听起来还可以.
我在>新展示位置中找到了新的展示位置的另一描述和具有const成员的类的分配
如果在对象的生存期结束之后且在存储之前 被占用的对象被重用或释放,一个新的对象是 在原始对象占用的存储位置创建的 指向原始对象的指针,引用的引用 原始对象的名称,否则原始对象的名称将 自动引用新对象,并且 新对象已启动,可以用于操作新对象,如果:
If, after the lifetime of an object has ended and before the storage which the object occupied is reused or released, a new object is created at the storage location which the original object occupied, a pointer that pointed to the original object, a reference that referred to the original object, or the name of the original object will automatically refer to the new object and, once the lifetime of the new object has started, can be used to manipulate the new object, if:
-
新对象的存储空间正好覆盖了原始对象所占据的存储位置,并且
the storage for the new object exactly overlays the storage location which the original object occupied, and
新对象与原始对象具有相同的类型(忽略顶级cv限定词),并且
the new object is of the same type as the original object (ignoring the top-level cv-qualifiers), and
原始对象的类型不是const限定的,并且,如果是类类型,则不包含任何类型为 const限定或引用类型,并且
the type of the original object is not const-qualified, and, if a class type, does not contain any non-static data member whose type is const-qualified or a reference type, and
原始对象是类型T的最大派生对象,而新对象是类型T的最大派生对象(也就是说,它们不是 基类子对象).
the original object was a most derived object of type T and the new object is a most derived object of type T (that is, they are not base class subobjects).
这似乎可以解释UB.但这是真的吗?
This seems to explain the UB. But is really true?
这不是我不能拥有std::vector<Base>
吗?因为我假设由于其预分配,所以std::vector
必须依赖placement-new
和显式ctor.第4点要求它是派生自最广泛的类型,而Base
显然不是.
Doesn't this mean, that I could not have a std::vector<Base>
? Because I assume due to its pre-allocation std::vector
must rely on placement-new
s and explicit ctors. And point 4 requires it to be the most-derived type which Base
clearly isn't.
推荐答案
我相信Elizabeth Barret Browning说得最好.让我数一数.
I believe Elizabeth Barret Browning said it best. Let me count the ways.
- 如果
Base
不可破坏,我们将无法清理资源. - 如果
sizeof(Derived)
大于动态类型this
的大小,我们将破坏其他内存. - 如果
Base
不是Derived
的第一个子对象,则新对象的存储将不会完全覆盖原始存储,并且您还将最终破坏其他内存. - 如果
Derived
与初始动态类型只是一个不同的类型,即使它的大小相同,也与我们在
- If
Base
isn't trivially destructible, we're failing to cleanup resources. - If
sizeof(Derived)
is larger than the size of the dynamic type ofthis
, we're going to clobber other memory. - If
Base
isn't the first subobject ofDerived
, then the storage for the new object won't exactly overlay the original storage, and you'd also end up clobbering other memory. - If
Derived
is just a different type from the initial dynamic type, even if it's the same size, than the object that we're callingfoo()
on cannot be used to refer to the new object. The same is true if any of the members ofBase
orDerived
areconst
qualified or are references. You'd need tostd::launder
any external pointers/references.
但是,如果sizeof(Base) == sizeof(Derived)
和Derived
是可微毁的,则Base
是Derived
的第一个子对象,而您实际上只有Derived
个对象……这很好.
However, if sizeof(Base) == sizeof(Derived)
, and Derived
is trivially destructible, Base
is the first subobject of Derived
, and you only actually have Derived
objects... this is fine.
这篇关于通过在此指针上放置new来重新初始化对象的未定义行为的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!