重用存储是否启动新对象的生命周期? [英] Does reuse storage start lifetime of a new object?

查看:111
本文介绍了重用存储是否启动新对象的生命周期?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

  #include< cstdlib> 
struct B {
virtual void f();
void mutate();
virtual〜B();
};
struct D1:B {void f(); };
struct D2:B {void f(); };
void B :: mutate(){
new(this)D2; // reuses storage - ends the lifetime of * this
f(); // undefined behavior - WHY ????
... = this; // OK,这指向有效的内存
}



我需要解释为什么 f()调用有UB? new(this)D2; 重用存储,但也调用 D2 的构造函数,目的。在这种情况下, f()等于 this - > f()这是我们只需调用 D2 f()成员函数。是UB?

解决方案

标准显示了这个例子§3.8 67 N3690:

  struct C {
int i;
void f();
const C& operator =(const C&);
};

const C& C :: operator =(const C& other){
if(this!=& other){
this->〜C //生存期*这结束
new(this)C(other); // C类创建的新对象
f(); // well-defined
}
return * this;
}

C c1;
C c2;
c1 = c2; // well-defining
c1.f(); // well-defined; c1引用类型为C的新对象

请注意,此示例终止对象的生命周期



但是,即使你这样做,标准也说:


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



- 新对象的存储正好覆盖原始对象占用的存储位置
,以及 - 新对象的
与原始对象的类型相同(忽略顶级
cv-qualifiers)和



- 原始对象的类型不是
const限定的,如果类类型不包含任何非静态
数据成员,其类型为const限定或引用类型,并且



- 原始对象是类型最大的派生对象T和
new对象是类型T的最派生对象(也就是说,它们不是
基类子对象)。


注意和字,以上条件必须全部满足。



由于您未满足所有条件对象置于基类对象的内存空间中),则在使用此指针的隐式或显式引用内容时,您具有未定义的行为



根据编译器的实现,这可能或可能会受到影响,因为基类虚拟对象为 vtable 就地构造一个派生类型的对象保留一些空格,一些虚函数意味着vtable可能是不同的,放置对齐问题和其他低级内部,你会有一个简单的sizeof不足以确定你的代码是否正确。 p>

#include <cstdlib>
struct B {
    virtual void f();
    void mutate();
    virtual ~B();
};
struct D1 : B { void f(); };
struct D2 : B { void f(); };
void B::mutate() {
    new (this) D2; // reuses storage — ends the lifetime of *this
    f(); // undefined behavior - WHY????
    ... = this; // OK, this points to valid memory
}

I need to be explained why f() invokation has UB? new (this) D2; reuses storage, but it also call a constructor for D2 and since starts lifetime of a new object. In that case f() equals to this -> f(). That is we just call f() member function of D2. Who knows why it is UB?

解决方案

The standard shows this example § 3.8 67 N3690:

struct C {
  int i;
  void f();
  const C& operator=( const C& );
};

const C& C::operator=( const C& other) {
  if ( this != &other ) {
    this->~C(); // lifetime of *this ends
    new (this) C(other); // new object of type C created
    f(); // well-defined
  }
  return *this;
}

C c1;
C c2;
c1 = c2; // well-defined
c1.f(); // well-defined; c1 refers to a new object of type C

Notice that this example is terminating the lifetime of the object before constructing the new object in-place (compare to your code, which does not call the destructor).

But even if you did, the standard also says:

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 — the new object is of the same type as the original object (ignoring the top-level cv-qualifiers), and

— 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

— the original object was a most derived object (1.8) of type T and the new object is a most derived object of type T (that is, they are not base class subobjects).

notice the 'and' words, the above conditions must all be fulfilled.

Since you're not fulfilling all the conditions (you have a derived object in-placed into the memory space of a base class object), you have undefined behavior when referencing stuff with an implicit or explicit use of this pointer.

Depending on the compiler implementation this might or might now blow because a base class virtual object reserves some space for the vtable, in-place constructing an object of a derived type which overrides some of the virtual functions means the vtable might be different, put alignment issues and other low-level internals and you'll have that a simple sizeof won't suffice to determine if your code is right or not.

这篇关于重用存储是否启动新对象的生命周期?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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