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
Constants and Compiler Optimization in C++
Catch Exception by Pointer in C++
How to Check Whether Operator== Exists
What Happens in a Double Delete
Is There Any Real Risk to Deriving from the C++ Stl Containers
C++ Code File Extension? Difference Between .Cc and .Cpp
When Vectors Are Allocated, Do They Use Memory on the Heap or the Stack
Tool to Track #Include Dependencies
How to Turn on (Literally) All of Gcc'S Warnings
Is It Safe to Link C++17, C++14, and C++11 Objects
What's the Best Way to Iterate Over Two or More Containers Simultaneously
How to Pass Variable Number of Arguments to Printf/Sprintf
C++ Performance Challenge: Integer to Std::String Conversion