2

I have asked this question. My question now is how this works? To elaborate, how can I point to an object that is not yet initialised. I have made this MWE and it shows that the object is copy created not copy assigned .i.e. the object is not yet initialised yet I am able to point to it.

#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;
}

Output:

constructor
0
constructor
101
101

Do not be afraid of getting into details of how the memory is handled. And, where every member resides.

Community
  • 1
  • 1
aiao
  • 4,621
  • 3
  • 25
  • 47
  • Please let me know if it is yet unclear – aiao Mar 19 '13 at 22:51
  • Sorry had a mistake editted it now `:BarParent(&foo)` – aiao Mar 19 '13 at 22:54
  • 1
    Have you tried printing the value of Foo in the BarParent constructor? – nonsensickle Mar 19 '13 at 23:02
  • 1
    one issue with your example is that you never actually use bar.p_foo, so it could be anything without any issues. – tletnes Mar 19 '13 at 23:03
  • 5
    My guess is that even though the member `Foo` has not been initialized, space has been allocated for it (and all other member variables) before the base constructor is called. This would allow you to pass a pointer to that space, even though it is uninitialized. – Porkbutts Mar 19 '13 at 23:03
  • As others have noted, it would be interesting to confirm the value of `bar.p_foo->x` – Porkbutts Mar 19 '13 at 23:06
  • @nonsensical that was helpful. It actually has `x=0`. But for me this is counter intuitive since it uses copy constructor and not the assignment operator – aiao Mar 19 '13 at 23:07
  • @aiao the reason the copy constructor is used is because this isn't an assignment. See http://www.geeksforgeeks.org/copy-constructor-vs-assignment-operator-in-c/ or the answer to the following SO question http://stackoverflow.com/questions/2462773/c-copy-construct-construct-and-assign-question – nonsensickle Mar 19 '13 at 23:10

2 Answers2

5

Don't mistake Initialization for Allocation. BarChild::foo will be allocated before the constructor is called since it is stored in place, so there will be a well defined location for BarParent::p_foo to point at. Foo's constructor will Initialize BarChild::foo, but as long as you don't try to read from BarChild::foo before the constructor is called you will not notice the ordering.

tletnes
  • 1,958
  • 10
  • 30
  • If the initialization of the members is not dependent on other members; this code is perfectly fine to use without unpleasant surprises. Is that right? – aiao Mar 19 '13 at 23:18
  • @aiao if the member is not read from until after it is initialized it is safe. this is slightly different from what you say, but in this example it is more or less the same. – tletnes Mar 19 '13 at 23:21
  • Could you elaborate a little more on who else can use the member other than initializations of other members. – aiao Mar 19 '13 at 23:23
  • code in another thread might use it, or initalizers from other members might call other code which could touch it. The more important bit to recall is that it is possible that foo might not even be initialized in the constructor, it might be a value that is only used in a certain state, and so is only initalized when the object enters that state. None of these are hte case in this example, so I think you are safe. – tletnes Mar 19 '13 at 23:27
3

At this line

BarChild bar(foo);

the compiler reserves enough stack space for a BarChild object then calls the constructor to begin the object's lifetime. Within the object the foo member has a fixed offset, every BarChild object has a foo member at the same offset within it, so since the this pointer has a known address within the constructor (it's the address of bar on the stack) then this->foo is also at a known address, even if the memory at that address hasn't been initialized yet. The BarChild obejct doesn't "grow bigger" as each member is initialized, its size is fixed and space for all the members is already "reserved" before they're initialized.

This is somewhat analogous to:

 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

The object doesn't exist yet, and it's not valid to use it, but its address is known in advance of it being initialized.

Jonathan Wakely
  • 166,810
  • 27
  • 341
  • 521