当我们开始覆盖内存时,对象的生命周期已经结束了吗?

时间:2014-09-08 04:51:20

标签: c++ constructor destructor

我无法自己解决以下问题:

假设我们以下列方式重用内存:

struct A 
{ 
    int a;
    A(){ }
    ~A(){ }
};

struct B : A 
{ 
    int b;
    B(){ }
    ~B(){ }
};

A *a = (A*) malloc(sizeof(A) + sizeof(B));
B *b = new (a) B; //Constructor of B is calling

a引用的对象的生命周期已经在B的构造函数开始调用之前结束,或者在B的构造函数完成后结束了吗?

4 个答案:

答案 0 :(得分:1)

您尝试使用展示位置新运算符初始化b。此运算符不首先调用类A的析构函数(a),而是将新的析构函数初始化为a指向的内存。这是有问题的(如评论中所述),因为sizeof(B)大于sizeof(A)并且您在指针sizeof(A)处仅分配了a。所以它是未定义的行为。

对象a的生命周期正式结束。你会得到类似的东西:

class A { int a; };
void* p = malloc(sizeof(A));
A* a1 = new (p) A();
A* a2 = new (p) A();

我认为,你会在同一个内存中得到类似double的析构函数,但这是编译器实现特有的。我不认为,标准确实对此有所说明。

答案 1 :(得分:1)

只要您输入 B的构造函数,a就不再指向对象A

原因是即使在对象的构造函数的第一条指令之前,运行时已经完成了VMT,基础子对象和成员初始化。

同样,如果B的构造函数由于异常而没有终止,则无论如何都已经使用了内存,并且最终存在于同一内存地址的另一个对象不再存在。< / p>

换句话说,就是不要这样做......在C ++中,对象不仅仅是一堆字节而且还有权利;例如,他们的析构函数被调用的权利; - )

答案 2 :(得分:0)

相关章节(3.8)中的标准说,

  

类型T的对象的生命周期在以下时间结束:

     

- 如果T是具有非平凡析构函数(12.4)的类类型,则析构函数调用开始,或

     

- 重用或释放对象占用的存储空间。

我解释为a指向的对象的生命周期在B的成员被初始化时结束。在此之前,存储不会被重复使用。

答案 3 :(得分:0)

当你删除它而不是之前结束的生命周期 - 因为强制两个不同的对象以这种方式占用相同的空间本身是未定义的行为,并且绝对不建议你的编译器可能会或可能不会将该内存视为可用于重用和覆盖b。

如果您需要占用相同位置的两个对象,例如:Message Variants或者您正在尝试编写自己的调试器,那么您应该使用union类型。

你不建议做这种事情的另一个原因是你会创造一个维护噩梦。例如稍后在您的代码中,您可以:

b.b = 3
while (a.a > 0) {
   b.b--;
}