In my code, I have a basic diamond pattern:
CommonBase
/ \
/ \
DerivedA DerivedB
\ /
\ /
Joined
It's implemented like this, with the common base class having a default constructor and a constructor taking a parameter:
struct CommonBase {
CommonBase() : CommonBase(0) {}
CommonBase(int val) : value(val) {}
const int value;
};
struct DerivedA : public virtual CommonBase {
void printValue() {
std::cout << "The value is " << value << "\n";
}
};
struct DerivedB : public virtual CommonBase {
void printValueTimes2() {
std::cout << "value * 2 is " << value * 2 << "\n";
}
};
struct Joined : public DerivedA,
public DerivedB {
Joined(int val) : CommonBase(val) {
std::cout << "Constructor value is " << val << "\n";
std::cout << "Actual value is " << value << "\n";
}
};
The Joined class initializes the virtual base using the constructor that takes a parameter, and everything works as expected.
However, when I derive a class from the Joined class, something strange happens - the CommonBase's default constructor is called unless I explicitly initialize CommonBase in the derived classes constructor as well.
This is demonstrated using this code:
struct JoinedDerivedA : public Joined {
JoinedDerivedA() : Joined(99) {
printValue();
}
};
struct JoinedDerivedB : public Joined {
JoinedDerivedB() : Joined(99), CommonBase(99) {
printValue();
}
};
int main() {
std::cout << "======= Joined =======\n";
Joined j(99);
j.printValue();
std::cout << "\n=== JoinedDerivedA ===\n";
JoinedDerivedA a;
std::cout << "\n=== JoinedDerivedB ===\n";
JoinedDerivedB b;
return 0;
}
The output of this code is
======= Joined =======
Constructor value is 99
Actual value is 99
The value is 99
=== JoinedDerivedA ===
Constructor value is 99
Actual value is 0 // <-- unexpected behaviour
The value is 0
=== JoinedDerivedB ===
Constructor value is 99
Actual value is 99
The value is 99
Why is this the case? Is it possible to not have to explicitly initialize the common base class in derived classes again?
Here's the code on ideone, so you can run it yourself: https://ideone.com/Ie94kb
This is specified in Initializing bases and members [class.base.init] (12.6.2 in draft n4567). We can read in §13 (emphasize mine):
(13) In a non-delegating constructor, initialization proceeds in the following order:
(13.1) — First, and only for the constructor of the most derived class (1.8), 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.
(13.2) — Then, direct base classes are initialized in declaration order as they appear in the base-specifier-list
(regardless of the order of the mem-initializers).
(13.3) — Then, non-static data members are initialized in the order they were declared in the class definition
(again regardless of the order of the mem-initializers).
(13.4) — Finally, the compound-statement of the constructor body is executed.
[ Note: The declaration order is mandated to ensure that base and member subobjects are destroyed in the
reverse order of initialization. —end note ]
That means that the virtual base class will be initialized before the initialization of Joined. So in DerivedJoinedA, it is default initialized with a value of 0. Then when initializing Joined, the initialization of CommonBase is ignored because it has already been initialized and value keeps its 0 value.
That is the reason why you have to initialize the virtual base classes in the most derived class.
Related
[dcl.init]/(6.2)
If T is a (possibly cv-qualified) non-union class type, its padding
bits are initialized to zero bits and each non-static data member,
each non-virtual base class subobject, and, if the object is not a
base class subobject, each virtual base class subobject is
zero-initialized;
AFAICT, the sentence above is confusing and dispensable. What do they mean by the object that is not a base class subobject?
The writing of a technical specification is a bit like programming. [dcl.init]/6 can therefore be thought of as a kind of function: the function which performs zero-initialization on an object. This function gets called when a complete object is zero-initialized.
Here's some pseudo-code for the general structure of [dcl.init]/6:
void zero_init(T &t)
{
if(zero_init_1(t)) return; //Executes rule 6.1; returns false if rule 6.1 doesn't apply to T.
if(zero_init_2(t)) return; //Executes rule 6.2; returns false if rule 6.2 doesn't apply to T.
if(zero_init_3(t)) return; //Executes rule 6.3; returns false if rule 6.3 doesn't apply to T.
...
}
The important thing here is zero_init_2, the code that represents section 6.2:
bool zero_init_2(T &t)
if(T is class && T is !union)
{
zero_init_padding_bit(t);
for(auto &base : non_virtual_bases(t))
zero_init(base);
for(auto &member : members(t))
zero_init(member);
if(!is_base_class(t))
for(auto &base : virtual_bases(t))
zero_init(base);
return true; //I did initialization here.
}
return false; //No initialization done here.
}
You suggested:
I would replace "and, if the object is not a base class subobject, each virtual base class subobject is zero-initialized" by "and each virtual base class subobject of the most derived object is zero-initialized".
The code for this would be as follows:
bool zero_init_2(T &t)
if(T is class && T is !union)
{
zero_init_padding_bit(t);
for(auto &base : non_virtual_bases(t))
zero_init(base);
for(auto &member : members(t))
zero_init(member);
auto &u = get_most_derived_object(t)
for(auto &base : virtual_bases(u))
zero_init(base);
return true; //I did initialization here.
}
return false; //No initialization done here.
}
In your suggested version, there is no conditional. So there is no conditional in the pseudo-code either. But zero_init is a function that can invoke zero_init_2; that is, it is recursive.
This means that virtual base classes could be zero-initialized multiple times. Indeed, it would be infinitely recursive on any type with virtual base classes. get_most_derived_object will return the most derived object. The non-union virtual base class subobjects of this object include... the one we're currently initializing. So we will recursively reinitialize ourselves, never terminating.
The original wording ensures that the virtual base classes will only be zero-initialized once. Yours does not.
"The object" is of type T. This sentence simply applies the logic of initializing virtual bases to zero-initialization. It is always the most derived object that initializes virtual bases. For instance.
class A {
//
};
class B : public virtual A {
//
};
class C : public B {
//
};
static B b; // The zero initialization of `b` also zero initializes an `A` sub-object
static C c; // The zero initialization of the 'B' sub-object **does not** include
// zero-initialization of an `A` sub-object.
The highlighted sentence serves a purpose here, and is not superfluous.
Example A:
#include <iostream>
struct VirtualBase
{
int x;
};
struct Derived : virtual VirtualBase
{};
struct Complete : Derived, virtual VirtualBase
{
Complete()
: VirtualBase{42}
, Derived()
{}
};
int main()
{
Complete c;
std::cout << c.x;
}
How does the initialization of Complete c; work?
We start with default-initialization of the Complete object. This calls the default ctor.
The default-ctor starts by initializing the virtual base classes (class.base.init).
We do an aggregate-initialization of the VirtualBase subobject and set the int to 42.
Then, we initialize the Derived subobject. The form of initialization is Derived() which causes value-initialization of the subobject.
We follow https://eel.is/c++draft/dcl.init#general-8.1.2 which causes the Derived subobject to be zero-initialized. Now we finally get to the OP.
if the object is not a base class subobject, each virtual base class subobject is zero-initialized;
The Derived object we're initializing is a base class subobject of Complete. Hence we do not zero-initialize the VirtualBase base class object of Derived.
Without the subclause "if the object is not a base class subobject", we would have zero-initialized the VirtualBase subobject of Derived. This would have meant to zero-initialize the int member, setting it to 0. Because this happens after we already wrote 42 into it, setting it to 0 would have overwritten the value.
Example B:
#include <iostream>
struct VirtualBase
{
int x;
};
struct Derived : virtual VirtualBase
{};
struct Complete : Derived, virtual VirtualBase
{
Complete()
: Derived()
{}
};
int main()
{
Complete c;
std::cout << c.x;
}
The constructor of Complete lacks any mem-initializer for VirtualBase, so it will default-initialize that virtual base class. Derived on the other hand is value-initialized, which causes zero-initialization. Since Derived is a base class subobject here, it will not zero-initialize the virtual base. It remains uninitialized and we get UB in the cout << c.x.
You get some funny output with gcc and clang but unfortunately I couldn't get the sanitizers to report on it. Live demo
I having hard time in solving those kind of question. In an exam I going to take in a few days, they show a program in C++ which has Multiple Inheritance:
struct X {
X(){cout << "X" << endl;}
};
struct A : virtual X {
int i;
A(){cout << "A" << endl;}
};
struct B : A {
int i;
B(){cout << "B" << endl;}
virtual void f() {cout << "f" << endl;}
};
struct C : A {
int i;
C(){cout << "C" << endl;}
C(int i){cout << "C2" << endl;}
virtual void g() {cout << "g" << endl;}
};
struct D : virtual B, virtual C {
int i;
D( int i) : C(i), B() {cout << "D" << endl;}
};
And show some code from main:
D* d = new D(2014);
C* c = d;
B* b = d;
And than ask "what will be the output?" I don't need the solution, I can always
just run or debug it but I need to understand the intuition because I will not have the debugger with me on the exam. Now I know the algorithm I need to follow:
5 Initialization shall proceed in the following order:
— First, and only for the constructor of the most derived class as
described below, virtual base classes shall be 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 class names in the derived class
base-specifier-list.
— Then, direct base classes shall be initialized in declaration order
as they appear in the base-specifier-list (regardless of the order of
the mem-initializers).
— Then, nonstatic data members shall be initialized in the order they
were declared in the class definition (again regardless of the order
of the mem-initializers).
— Finally, the body of the constructor is executed. [Note: the
declaration order is mandated to ensure that base and member
subobjects are destroyed in the reverse order of initialization. ]
But I really having bad time solving them, it does not make any sense. Is it possible to show the intuition, maybe a trick or tip on solving those kind of questions? Maybe to show dark corners? Maybe somehow to write down all the classes/structs, write the vbase classes somehow in order to easy see to output?
the code is printing all the constructors. i read that constructors are not inherited when we derive a class from another class. then why creation of c is invoking constructors from b and a
class A
{
public:
A() { cout << "A's constructor called" << endl; }
};
class B
{
public:
B() { cout << "B's constructor called" << endl; }
};
class C: public B, public A // Note the order
{
public:
C() { cout << "C's constructor called" << endl; }
};
int main()
{
C c;
return 0;
}
When the document you read said constructors are "not inherited", what it means is that if class A defines a constructor A::A(int x), then a child class B will not automatically have a constructor that takes an int.
However, it's still necessary to initialize the values of the parent class; otherwise, the parent object might be in an invalid state. Constructors are used to initialize classes, so means one of the parent class' constructors must be called from the child constructor's initializer list. If the parent class has a default constructor, that one gets called by default. That's what you see in your example. If the parent doesn't provide a default constructor, you have to specify which one you want called:
class A
{
public:
A(int x) { cout << "A's constructor called" << endl; }
};
class C: public A
{
public:
C()
: A(7) /* compilation will fail without this line */
{ cout << "C's constructor called" << endl; }
};
Constructors are not inherited in the traditional sense.
Classes are what's inherited.
But in order to construct a class, its constructor needs to be called. That's its job. Hard rule, no exceptions.
When you inherit one class from a second class, constructing the first class requires the second class to be constructed too. Because the first class always contains the second class. Another hard rule, no exceptions. That's what "inheritance" means.
So, constructing the first class will invoke its constructor. Then, to construct the second class its constructor will also need to be called (actually the second class gets constructed first, then the first class's construction takes place).
And that's why both constructors will be used.
i read that constructors are not inherited when we derive a class from another class
That is correct. However, you seem to have misunderstood the meaning of that.
Let's say you have:
struct A
{
A(int) {}
};
struct B : A
{
B() : A(0) {}
};
Given the above, you won't be able to use:
B b(10);
since A(int) is not inherited by B.
That's the crux of your misunderstanding.
then why creation of c is invoking constructors from b and a
However, when you construct a B, a constructor of B is called to initialize its members. A constructor of A must also be called so that the sub-object of B that corresponds to A can be initialized.
There are couple of ways to initialize the A-part of B.
You can use a constructor of A explicitly in the member initialization list by using the syntax:
B() : A(0) {}
Leave the member initialization empty, in which case the default constructor of A is called.
B() {}
That is equivalent to:
B() : A() {}
In the example I presented, that will result in a compiler error since the default constructor of A has been deleted by providing another constructor that is different than the default constructor.
Coming back to your implementation of the default constructor of C, you have:
C() { cout << "C's constructor called" << endl; }
That is equivalent to
C() : B(), A() { cout << "C's constructor called" << endl; }
B::B() and A::A() are called when an instance of C is constructed.
Constructors are called when classes are inherited. The inheritance basically gives the derived class instance anonymous member instances of the base classes, amongst other things. These instances need to be constructed so their constructors are called.
"Constructors are not inherited" means, that class C should and will have it's own constructors, despite fact that there were constructor of B, it will not be able to use constructor of B instead of constructor of C.
And that's exactly what you get: you get constructors of all parent classes.
When you have hierarchy of classes, and construct object from one, there will be sequential construction of all his parents, starting from the base one.
And when you will destroy them, there will be sequential destruction of him and all his parents, starting from him.
By rule: first created -- last destructed.
By not inherited, C++11 standard means this
class A
{
public:
A(int x) {}
};
class B: public A
{
};
int main(void)
{
B b(5);
return 0;
}
This will fail to compile because A(int) is not inherited. You can define B to explicitly inherit A(int) by
class B: public A
{
using A::A;
};
In your case you are defining all default ctors, and which explicitly defined or not, still exist, and will be called as part of the object initialization due to your C c declaration.
C++ inheritance basically creates a class made of parts of its super-classes. For example:
class A {
public:
A() {
std::cout << "Constructor A" << '\n';
}
};
class B : public A {
public:
B() {
std::cout << "Constructor B" << '\n';
}
};
class C : public B {
public:
C() {
std::cout << "Constructor C" << '\n';
}
};
Class C is actually class C, with a class B part, and a class A part. So in order to construct class C, we need to construct each of its parts by calling the constructors for those parts. The order of these constructors is from the most-base class to the most-derived class (in this case A to C). Most-base being the class at the top of the inheritance tree, and most-derived being the class at the bottom.
This same rule applies to destructors as well. The only difference is that the destrutors are called from most-derived to most-base (C to A).
I read the following piece of code somewhere, as an example to solve the diamond problem in case of multiple inheritance :
#include<iostream>
using namespace std;
class A
{
int x;
public:
A() {}
A(int i) { x = i; }
void print() { cout << x; }
};
class B: virtual public A
{
public:
B():A(10) { }
};
class C: virtual public A
{
public:
C():A(100) { }
};
int main()
{
D d;
d.print();
return 0;
}
Suppose class D is defined as follows :
class D: public B, public C
{
public:
D():B(),C(){}
};
I get some garbage value in the print. And if the class D is defined as follows (parameterized constructor for A is explicitly called) :
class D: public B, public C
{
public:
D():B(),C(),A(20){}
};
I get 20 as output. In the first case, I can understand that the default A() constructor is called, hence the garbage value as x is not set to any value.
However, in the second case, its not clear. When is the parameterized constructor for A(int) is called? If I understood correctly, call order depends on order of inheritance. Since B is inherited first, B's constructor call takes precedence over C.Since B inherits A, A() will be called first, of all. Then B's constructor will be called. Then C's constructor will be called. At last, A(int) will be called, as A's constructor is called explicitly in class D. If this is the case, then the output is well justified for the second case. However, this, then contradicts the output for the below piece of code :
#include<iostream>
using namespace std;
class Person {
public:
Person(int x) { cout << "Person::Person(int ) called" << endl; }
Person() { cout << "Person::Person() called" << endl; }
};
class Faculty : virtual public Person {
public:
Faculty(int x):Person(x) {
cout<<"Faculty::Faculty(int ) called"<< endl;
}
};
class Student : virtual public Person {
public:
Student(int x):Person(x) {
cout<<"Student::Student(int ) called"<< endl;
}
};
class TA : public Faculty, public Student {
public:
TA(int x):Student(x), Faculty(x), Person(x) {
cout<<"TA::TA(int ) called"<< endl;
}
};
int main() {
TA ta1(30);
}
The output for this program :
Person::Person(int ) called
Faculty::Faculty(int ) called
Student::Student(int ) called
TA::TA(int ) called
Why Person(int) called at the beginning in this case, and not at the last?
N4594 12.6.2/13:
In a non-delegating constructor, initialization proceeds in the following order:
First, and only for the constructor of the most derived class (1.8), 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 .
Then, direct base classes are initialized in declaration order as they appear in the base-specifier-list
(regardless of the order of the mem-initializers).
Then, non-static data members are initialized in the order they were declared in the class definition
(again regardless of the order of the mem-initializers).
Finally, the compound-statement of the constructor body is executed.
[ Note: The declaration order is mandated to ensure that base and member subobjects are destroyed in the
reverse order of initialization. —end note ]
Construction always starts from the base class. If there are multiple base classes then, it starts from the left most base. (side note: If there is a virtual inheritance then it's given higher preference). Then it comes the turn for member fields. They are initialized in the order they are declared. At the last the class itself is constructed.
The order of destructor is exactly reverse
Since B inherits A, A() will be called first, of all. Then B's
constructor will be called.
This is not quite true when A is virtually inherited.
When a class is virtually inherited, it is effectively inherited by the most derived class, for the purpose of invoking constructors and destructors. That's what virtual inheritance between.
Since D derives from B and C, in that class hierarchy D inherits A when it comes to invoking constructors and destructors, because D is the most-derived class.
With virtual inheritance, constructor of the virtual class is called only in the most derived class.
And order of initialization doesn't depend of order of initialization list, but to the order of declaration inside the class.
This question already has answers here:
Are parent class constructors called before initializing variables?
(7 answers)
Closed 7 years ago.
I have the following code:
class A{
public:
A(int* i){
std::cout << "in A()" << i << std::endl;
}
};
class B: public A{
public:
B(): i{new int{10}}, A{i}{
std::cout << "in B()" << std::endl;
}
private:
int* i;
};
int main()
{
B b;
}
In A constructor I have 0 (which is expected). But I want to initialize i before. Is it possible at all?
i is a data member of class B, so in order to be created, an object of class B has to be created first. So the answer, is no.
No, it is not possible since the base class initialization is always prior to derived class initialization.
C++11, 12.6.2
10 In a non-delegating constructor, initialization proceeds in the following order:
First, and only for the constructor of the most derived class (1.8), 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.
Then, direct base classes are initialized in declaration order as they appear in the base-specifier-list (regardless of the order of the mem-initializers).
Then, non-static data members are initialized in the order they were declared in the class definition (again regardless of the order of the mem-initializers).
Finally, the compound-statement of the constructor body is executed.
Initialize member before base constructor. Possible?
No, that is not possible. The standard mandates that the base classes be initialized first before the members of the class are initialized.
B(): i{new int{10}}, A{i}{
std::cout << "in B()" << std::endl;
}
is transformed to:
B(): A{i}, i{new int{10}}{
std::cout << "in B()" << std::endl;
}