Home > Enterprise >  Pass derived class members to base class constructor
Pass derived class members to base class constructor

Time:11-02

I have the following piece of code that is working but I don't understand why it works (inspired by a real life code base):

Base class definition:

class Pointers {
private:
    int* Obj1;
    double* Obj2;
public:
    Pointers(int* Obj1_, double* Obj2_) : Obj1{Obj1_}, Obj2{Obj2_} {}
};

We now derive a class from our base class where we shadow the two pointers with an int and a double of the same name:

class Objects : public Pointers {
public:
    int Obj1{69};
    double Obj2{72};
    Objects() : Pointers(&Obj1, &Obj2) {}
};

Within the constructor of Objects we call the constructor of Pointers and pass the adresses of Obj1 and Obj2. This actually works: The (shadowed) pointers will point to Obj1 (69)and Obj2 (72).

My question is: Why is this working? I thought that in the first step the base class members are constructed (which are the two pointers) and only after that the derived class members (the int and double with the same name) are constructed. How can we pass these objects adresses to the base class constructor in the first place?

CodePudding user response:

It works, because at the moment when you call your base class constructor, the derived class members

    int Obj1{69};
    double Obj2{72};

are not yet initialized, but their addresses are already known, and you are using their addresses to initialize pointers in the base class. Note that it has little to do with shadowing.

Of course if you try to print the pointed values in Pointers constructor

    Pointers(int* Obj1_, double* Obj2_) : Obj1{Obj1_}, Obj2{Obj2_} {
        std::cout << "Obj1: " << *Obj1 << ", Obj2: " << *Obj2 << std::endl;
    }

you will get some garbage (UB), because pointed to values are not yet initialized. To make the code more clear avoid member fields assignments in the class body, rather use the constructor member initializer list:

Objects() : Pointers(&Obj1, &Obj2), Obj1{69}, Obj2{72} {}

CodePudding user response:

Here:

class Objects : public Pointers {
public:
    int Obj1{69};
    double Obj2{72};
    Objects() : Pointers(&Obj1, &Obj2) {}
};

Obj1 refers to Objects::Obj1. If there is a same named member in Pointers then it is shadowed by the member in Objects. You need to qualify the name Pointers::Obj1. You don't do that, so Obj1 refers to Objects::Obj1. Nothing surprising here.

Then here:

class Pointers {
private:
    int* Obj1;
    double* Obj2;
public:
    Pointers(int* Obj1_, double* Obj2_) : Obj1{Obj1_}, Obj2{Obj2_} {}
};

Nothing is shadowed, Obj1_ and Obj1 are distinct identifiers. The base class initializes its members with the pointers you pass to the constructor.

This seems to indicate that when the ctor of Pointers is called within the ctor of Objects it is already known at which adresses in memory the members of the derived class will be?

Yes. You can use addresses and references of members of objects that are under construction, before those members are initialized. As long as you do not dereference the pointer (or try to read the member's value via the reference) all is fine. For example the code would invoke undefined behavior, if in the base constructors body you would add some std::cout << *Obj1;, because the int pointed to by Obj is not initialized at that point.

CodePudding user response:

The initialization of the class actually proceeds as follows:

  1. Determine the memory that's used to construct the object (e.g. stack location or use of the result of the new operator).
  2. Enter call of Object::Object()
  3. Pointers::Pointers(int* Obj1_, double* Obj2_) gets called. This happens before any initializers for the members of Object are executed. The memory locations are already available though (see step 1.), so the address-of operator can be used.
  4. Initialization of Pointers part of the object completes
  5. The member variables of Object are initialized.

Note simply that simply passing the address of member variables can be done, but you need to make sure the pointers are not dereferenced in the base class constructor, since this would result in undefined behaviour.

  • Related