How to Initialize Base Class Member Variables in Derived Class Constructor

How can I initialize base class member variables in derived class constructor?

You can't initialize a and b in B because they are not members of B. They are members of A, therefore only A can initialize them. You can make them public, then do assignment in B, but that is not a recommended option since it would destroy encapsulation. Instead, create a constructor in A to allow B (or any subclass of A) to initialize them:

class A 
{
protected:
A(int a, int b) : a(a), b(b) {} // Accessible to derived classes
// Change "protected" to "public" to allow others to instantiate A.
private:
int a, b; // Keep these variables private in A
};

class B : public A
{
public:
B() : A(0, 0) // Calls A's constructor, initializing a and b in A to 0.
{
}
};

Initialize Derived class member variable before calling Base class constructor. Is this UB?

I would like to initialize a member variable of a Derived class, and after that pass it to the Base class constructor.

In C++, the order of construction is of the base part(s) before the derived parts. This is because it is far more common for the derived parts to (potentially) be constructed in terms of the base parts. In order to make this well-defined, the base-then-derived order is specified. Using derived in the base is undefined, therefore.

If you want your base to use derived members, there's a way to ensure the order is OK. Make the "members" base classes too. Note that boost::base_from_member is built exactly for making this more convenient.

Say you have some

class member_type{};

And you'd like to have derived have a member_type member, and derive from base. Then you could use:

class derived : 
private boost::base_from_member<member_type>,
public base {
using my_member_type = private boost::base_from_member<member_type>;

public:
derived();
};

Note that now derived subclasses both my_member_type and base (in that order). Hence, the latter can use the former in its construction.

derived::derived() : 
my_member_type{3},
base{my_member_type::member} {
}

Initialization of a base class reference from a derived class member

The base class Base of Derived is constructed before the member data.

As a result data will not be initialized when you pass a reference to it to Base's constructor. The initialization will happen after that constructor call.

You are however trying to read the member x of data in Base's constructor. At this point data's lifetime has not started yet and accessing the value of a non-static data member of an object outside its lifetime causes undefined behavior.

Whether or not the assertions succeed isn't significant. Undefined behavior allows for either outcome.

The situation would be potentially different (although technically not rules in the standard) if you were not trying to access the value of data inside Base's constructor, but only storing the reference to it.

Initialize base class with data member from derived class

The trick is to derived from one more base class who's function is to own the X.

Note that base class declaration order matters:

struct X {
X (int n) {}
};

struct Base {
Base(X & x) {}
};

struct OwnsAnX {
OwnsAnX(int n) : x_(n) {}

X& get_x() { return x_; }
private:
X x_;
};

struct Derived
: OwnsAnX // note: order is important
, Base
{
Derived()
: OwnsAnX(2)
, Base(get_x())
{}

// x is accessed through the inherited get_x()
};

but it's error prone if you don't keep the correct order of the classes you're inheriting from

This is a valid concern of the OP. The solution is to enable the compiler warning -Wreorder.

Reversing the order of the base classes then yields:

<source>: In constructor 'Derived::Derived()':
<source>:24:23: warning: base 'OwnsAnX' will be initialized after [-Wreorder]
, Base(get_x())
^
<source>:24:23: warning: base 'Base' [-Wreorder]
<source>:22:9: warning: when initialized here [-Wreorder]
Derived()
^~~~~~~

Order of member variables initialization of the base class in the derived class (constructor)

Before I explain the problem, let me say this: if your object's initialization is so complex that it requires the detailed knowledge I'm about to relate, then fix that. Overcomplicating your initialization is to nobody's benefit.

Now, let's look at what happens when you construct an object of type B by calling its default constructor.

Since B::A is not mentioned in the member initializer list, it will be default initialized. That will call A(int) with its default value of 0. The result of this will be initializing B::A::m_i with the value 0, followed by outputing that value, followed by incrementing B::A::m_i to the value 1.

Now, we initialize the members of B. The first member to be initialized is B::m_x. The member initializer for this reads and increments the value of B::A::m_i, which is a validly initialized value that happens to be 1 before this operation. So before calling A(int) for B::m_x, B::A::m_i takes the value 2.

B::m_x is initialized via a call to A(int) with the value 2. It stores that value in B::m_x.m_i, outputs it, and increments it by one.

Next, we initialize B::m_a. The member initializer initializes it via a call to new A[2], which will default construct 2 instances of A. These are all completely irrelevant to your question, as they are in no way related to B::A::m_i. But it does output two zeros.

After initializing B::m_a, we enter B's default constructor itself.There, it simply outputs the value of B::A::m_i. Which if you recall, was last set to 2. So that's what it outputs.



Related Topics



Leave a reply



Submit