通过在此指针上放置new来重新初始化对象的未定义行为 [英] Undefined behaviour on reinitializing object via placement new on this pointer

查看:143
本文介绍了通过在此指针上放置new来重新初始化对象的未定义行为的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我在cppcon上看到了一个

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 realloced 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-news 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.

  1. 如果Base不可破坏,我们将无法清理资源.
  2. 如果sizeof(Derived)大于动态类型this的大小,我们将破坏其他内存.
  3. 如果Base不是Derived的第一个子对象,则新对象的存储将不会完全覆盖原始存储,并且您还将最终破坏其他内存.
  4. 如果Derived与初始动态类型只是一个不同的类型,即使它的大小相同,也与我们在
  1. If Base isn't trivially destructible, we're failing to cleanup resources.
  2. If sizeof(Derived) is larger than the size of the dynamic type of this, we're going to clobber other memory.
  3. If Base isn't the first subobject of Derived, then the storage for the new object won't exactly overlay the original storage, and you'd also end up clobbering other memory.
  4. 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 calling foo() on cannot be used to refer to the new object. The same is true if any of the members of Base or Derived are const qualified or are references. You'd need to std::launder any external pointers/references.

但是,如果sizeof(Base) == sizeof(Derived)Derived是可微毁的,则BaseDerived的第一个子对象,而您实际上只有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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆