Lets say we have a Base class and a Derived class:
class Base {
public:
Base(int x) : var1(x)
int process(){
//return some function of var1
}
protected:
int var1;
}
class Derived : public Base {
Derived(int init) : Base(init), a(process()), b(process()) {}
protected:
int a;
int b;
}
In other words, when we initialize Derived, we assume that the Base constructor is called first, which initializes the var1 member variable in the base class with the init value, and then the a and b member variables are initialized using the process() function, which depends on the value set to var1.
Notice that for this code to be correct we need to make sure that Base(init) is called before a(process()) and b(process()).
Is it valid to assume in C++ that the above initialization order will be maintained? Or do I need to change the Derived constructor to the following to guarantee the order of initialization?
Derived(int init) : Base(init) {
a = process();
b = process();
}
The order is guaranteed, you do not have to do anything. Bases are initialized before members (in the order in which they were declared). Members are initialized in the order in which they were declared [class.base.init]/13 (note: order of declaration, not the order in which the mem-initializers appear in the constructor's initializer list!).
You can also rely on bases and members being destroyed in the exactly opposite order of the order in which they were initialized…
Related
Say I have a base and derived class like this:
class Base {
public:
Base() { ... };
Base(int param) { ... };
};
class Derived : public Base {
public:
Derived() { ... };
Derived(int param) { ... };
Derived(int p1, int p2) { ... };
};
Note that I'm not explicitly calling any of Base's constructors for Derived's constructors. That is to say, I didn't write Derived() : Base() { ... } or Derived(int param) : Base(param) { ... }.
Do any of Base's constructors get called by default when creating an instance of Derived?
If so, does Base(int param) get called when using Derived(int param), or does Base() get called?
Put another way, does C++ always default to using a base class's constructor with the same signature as the derived class's constructor if you don't specify which constructor to use? Or does it just use the base class's default constructor?
If the former, what about when using a constructor in the derived class that doesn't have a matching constructor with the same signature in the base class, such as Derived(int p1, int p2)?
Please note this question does not relate to initialization of member variables of either class. I intentionally did not include any member variables in my pseudo-code. It specifically has to do with which constructor on the base class gets used if you do not explicitly specify a base constructor in the derived class's constructors.
Quoting from cppreference's description of constructors and how inheritance relates to them:
Before the compound statement that forms the function body of the constructor begins executing, initialization of all direct bases, virtual bases, and non-static data members is finished. Member initializer list is the place where non-default initialization of these objects can be specified. For bases and non-static data members that cannot be default-initialized, such as members of reference and const-qualified types, member initializers must be specified. No initialization is performed for anonymous unions or variant members that do not have a member initializer.
(Emphasis added.)
If you do not specify an initialization of the base class subobject in your derived class's constructor's member initializer list, the base class subobject is default-initialized.
Consider:
#include <iostream>
using namespace std;
class A {// base class
private:
int data;
public:
A(int data = 0)
{
this->data = data;
}
void show()
{
cout << data << endl;
return;
}
};
class B : virtual public A {
public:
B(int data = 0) :
A(data) {
}
};
class C : virtual public A {
public:
C(int data = 0) :
A(data) {
}
};
class D : public B, public C {
public:
D(int dataB = 0, int dataC = 0) :
B(dataB),
C(dataC) {
}
};
int main() {
D d(1, 2);
d.B::show();
d.C::show();
return 0;
}
The above code is the diamond class inheritance diagram. The base class is A. I use virtual inheritance to avoid the diamond problem. But why is the output of this program 0,0, not 1,2 as I expect?
B's constructor is passed data=1, and in its initializer list it calls A with data. C's constructor similar is passed data=2 and its initializer list it calls A with data.
We then ask the B and C subobjects to show their value. And we get 0 0 not 1 2 as I expect.
When you have this scheme with virtual inheritance, it is up to the most derived class in the hierarchy (in this case D) to call the constructor of the common base (A)1,2.
Since your constructor for A has a default parameter data = 0, it can be used as a default constructor. And this is what's happening, the common A sub-object gets default constructed, since you omitted it from D's member initialization list.
If you remove the default value for data, you'll get a nice compiler error for emphasis:
A(int data)
{
this->data = data;
}
On g++ it results with:
main.cpp: In constructor 'D::D(int, int)':
main.cpp:37:16: error: no matching function for call to 'A::A()'
C(dataC) {
1 Remember that with virtual inheritance there is only one sub-object of type A. And both the B and C sub-objects refer to it. It is impossible for your calls to show to print different things, since they access the same data. That is why it's up to the most derived class, so there is no ambiguity.
[class.mi/4]
A base class specifier that does not contain the keyword virtual specifies a non-virtual base class. A base class specifier that contains the keyword virtual specifies a virtual base class. For each distinct occurrence of a non-virtual base class in the class lattice of the most derived class, the most derived object shall contain a corresponding distinct base class subobject of that type. For each distinct base class that is specified virtual, the most derived object shall contain a single base class subobject of that type.
[class.base.init/13.1]
In a non-delegating constructor, initialization proceeds in the following order:
First, and only for the constructor of the most derived class, virtual base classes are initialized in the order they appear on a depth-first left-to-right traversal of the directed acyclic graph of base classes, where “left-to-right” is the order of appearance of the base classes in the derived class base-specifier-list.
...
2 So if you want to construct A with specific data you'd define D::D() like this instead:
D(int dataA = 0) :
A(dataA) {
}
When you have virtual inheritance, the virtual base class is initialized by the constructor of the most derived class.
D(int dataB = 0, int dataC = 0) :
B(dataB),
C(dataC) {}
is equivalent to:
D(int dataB = 0, int dataC = 0) :
A(),
B(dataB),
C(dataC) {}
which is, in your case, the same as
D(int dataB = 0, int dataC = 0) :
A(0),
B(dataB),
C(dataC) {}
Unless you construct an instance of B,
B(int data = 0) :
A(data) {
}
is the same as
B(int data = 0) {}
with no code to initialize A since A is already initialized in the constructor of D.
Same thing applies to the implementation of C::C(int data).
That explains the output you are seeing.
I think you misunderstood the concept of virtual inheritance and the 'diamond problem'. With virtual inheritance, you create a diamond inheritance pattern, but from your post, it appears that you want to avoid that and instead have two bases A, one from B and another from C. To obtain that, simply avoid virtual inheritance in B and C, when your code will write 1 2.
Incidently, if only B has virtual inheritance from A but C conventional inheritance from A, then D will have two bases A, but that from B is again default initialised (as explained in the other answers).
I have a in-class initialized const member in a derived class which I'd like to pass to the constructor of the base class.
Example:
class Base{
public:
Base(int a) : i(a){}
private:
int i;
};
class Derived : Base{
public:
Derived() : Base(a){}
private:
const int a = 7;
};
int main(){
Derived d;
}
However this spawns an uninitialized error:
field 'a' is uninitialized when used here [-Wuninitialized]
I was under the impression that const initializing it would set the value directly allowing it to be passed from the derived ctor in this manner. Am I doing something wrong or am I under the wrong impression? When are the const in-class initialized members initialized?
When initializing base classes and class members during creation of an object, the order of initialization is:
Virtual base classes, in tree order
Direct non-virtual base classes, in order
Non-static data members, in their order of declaration.
So Base(a) happens before a = 7 happens.
One way to fix this would be to make a be static const or static constexpr. This is probably a good idea anyway, because non-static const variables make your class more difficult to use. (e.g. there will not be an implicitly-generated copy-constructor).
Your question,
When are the const in-class initialized members initialized?
is a bit of a red herring. "in-class initialized" doesn't really mean anything; the brace-or-equal initializer is essentially just syntactic sugar and takes the place of the corresponding constructor initalizer list slot. const also has no special bearing. So the real question should be:
When are non-static data members initialized?
The details don't actually matter so much, suffice to say that non-static data members are initialized after base subobjects are initialized, so your proposed construction cannot work.
The straight-forward answer is not to use a brace-or-equals-initializer and just use a normal (possibly defaulted) constructor parameter. Here are a few examples:
struct Foo : Base
{
const int a;
// Default constructor, mention value only once
Foo(int _a = 10) : Base(_a), a(_a) {}
// DRYolent default constructor
Foo() : Base(10), a(10) {}
// Delegating default constructor
Foo() : Foo(10) {}
private: Foo(int _a) : Base(_a), a(_a) {}
};
Alternatively, if the value of the constant doesn't need to be configurable, then you can make it a per-class constant (rather than per-object):
struct Foo : Base
{
static const int a = 10;
Foo() : Base(a) {}
};
const int Foo::a; // only if you ODR-use Foo::a
#include<iostream.h>
class A{
public:
int i;
A(int j=3):i(j){}
};
class B:virtual public A{
public:
B(int j=2):A(j){}
};
class C:virtual public A{
public:
C(int j=1):A(j){}
};
class D:public B, public C {
public:
D(int j=0):A(j), B(j+1), C(j+2){}
};
int main()
{
D d;
cout<<d.i;
return 0;
}
I am not being able to understand how the final output is zero. Every time j is initialized in default way to some fixed value, how is the value initialized in the constructor of class D being passed to class A?
Since A is a virtual base class, it should be constructed only once, so it is not possible to create it with different constructor parameters, and the C++ compiler has to choose one way of creating a base class.
The obvious question is: which one is used?
And the rule is: the one specified in the most derived class that inherits A directly.
The initialization order is simple: first A (with the parameter value from D constructor initialization list), then B (it is D's first ancestor; and it uses the instance of A created before), then C (and it shares the same A instance), finally D (and it also shares the same A object as B and C).
The rule with virtual base inheritance is:
"The most derived class in a hierarchy must construct a virtual base"
In your case, from the most derived class D You explicitly called the constructor of A by passing an argument 0 So it sets the i to 0. As mentioned in rule virtual base class is constructed through most derived class only and the other constructor calls through intermediate hierarchy have no effect since it is only constructed once.
The order of calling is:
A(int)
B(int)
C(int)
Good Read:
Why virtual base class constructors called first?
such is the code,and with the error:"illegal member initialization: 'a' is not a base or member",what is the meaning of the error info,and why??
class A{
public:
int a;
};
class B:public A{
public:
B();
};
B::B():a(10){ // put "a(10)" into the constructor body is right
}
By the time the constructor of the derived class is invoked, the base class must already be constructed. So it's already too late. To see why it must be this way, consider:
class Base
{
public:
int i;
Base(int q) : i(q) { ; }
};
class Middle : public Base
{
public:
Middle() : Base(2) { printf("i=%d\n", i); }
};
class Derived : public Middle
{
public:
Derived() : i(3) { ; }
}
Now, think about it. The Middle constructor has to run before the Derived constructor. And the Middle constructor ensures that i is 2. So how can the Derived constructor later re-construct it with a different value?
See this answer for a more complete explanation of what's going on here. Basically, you cannot initialise A::a in the initialiser of B, because it is ill-formed according to the standard.
One more important piece of information - remember that if a class does not initialise a member object via the constructor initialization list, then the default constructor for that member will be invoked before that first statement in the base class constructor is executed. When you use the initialiser, what you are actually doing is specifying a constructor to be used INSTEAD of the default constructor.
Clearly when you are constructing a derived class, the parent class member has thus already been constructed, so you cannot reconstruct the base member again, which is what you are trying to do in this example by including it in the derived class initialisation list.
As an aside, if you all you want is to have the derived class be able to set the value of A::a to a specific value, then use the following code instead:
B::B()
{
a = 10;
}
Because the base class may want to initialize things its own way. The proper method is to call on of the base constructors in the initializer list and let the base ctor initialize itself.