2

我已经问过这个问题。我现在的问题是这是如何工作的?详细地说,我怎样才能指向一个尚未初始化的对象。我已经制作了这个 MWE,它表明该对象是创建的副本而不是分配的副本。即该对象尚未初始化但我能够指向它。

#include <iostream>

class Foo {
public:
    int x;

    Foo(const Foo& ori_foo) {
        std::cout << "constructor" << std::endl;
        x = ori_foo.x;
    }

    Foo& operator = (const Foo& ori_foo) {
        std::cout << "operator =" << std::endl;
        x = ori_foo.x;
        return *this;
    }

    Foo(int new_x) {
        x = new_x;
    }
};

class BarParent {
public:
    Foo *p_foo;

    BarParent(Foo* new_p_foo) : p_foo(new_p_foo)
    {
       std::cout << (*new_p_foo).x << std::endl;
    }
};

class BarChild : public BarParent {
public:
    Foo foo;

    BarChild(Foo new_foo)
        :BarParent(&foo) //pointer to member not yet initialised
        ,foo(new_foo) // order of initilization POINT OF INTEREST
        {}
};

int main()  {
    Foo foo(101);

    BarChild bar(foo);

    std::cout << bar.p_foo->x << std::endl;
std::cout << bar.foo.x << std::endl;
}

输出:

constructor
0
constructor
101
101

不要害怕了解如何处理内存的细节。而且,每个成员都居住在哪里。

4

2 回答 2

5

不要将初始化误认为是分配。BarChild::foo 将在调用构造函数之前分配,因为它存储在适当的位置,因此将有一个明确定义的位置供 BarParent::p_foo 指向。Foo 的构造函数将初始化 BarChild::foo,但只要您在调用构造函数之前不尝试从 BarChild::foo 读取,您就不会注意到排序。

于 2013-03-19T23:07:34.067 回答
3

在这一行

BarChild bar(foo);

编译器为对象保留足够的堆栈空间,BarChild然后调用构造函数开始对象的生命周期。在对象中,foo成员具有固定的偏移量,每个BarChild对象在其中都有一个foo位于相同偏移量的成员,因此由于this指针在构造函数中具有已知地址(它是bar堆栈上的地址),this->foo因此也位于已知地址,即使该地址处的内存尚未初始化。初始化每个成员时,BarChild对象不会“变大”,它的大小是固定的,并且所有成员的空间在初始化之前已经“保留”。

这有点类似于:

 char raw_memory[sizeof(Foo)];  // define block of uninitialized memory
 char* addr = raw_memory;       // take address of uninitialized memory
 new (raw_memory) Foo;          // initialize memory

该对象还不存在,使用它是无效的,但它的地址在它被初始化之前是已知的。

于 2013-03-19T23:07:25.897 回答