I don't understand why the output of this program is as follows. Why isn't there a compilation error? I thought when trying to construct B, the compiler would find no function called foo() and report an error.
#include <iostream>
using namespace std;
struct A{
int a;
A(int i=0) : a(i) { cout << "A" << endl; }
~A() { cout << "Bye A" << endl; }
int foo() { return a; }
};
struct B{
int b;
B(int i=0) : b(i) { cout << "B" << endl; }
~B() { cout << "Bye B" << endl; }
int bar() { return b; }
};
struct C : B, A {
C(int i=0) : B(foo()), A(i) {}
};
int main() {
cout << C(10).bar() << endl;
return 0;
}
The output:
B
A
0
Bye A
Bye B
In general, I would like to know when there is multiple inheritance, what is the order in which the parent structs are constructed and initialized? Can I expect a similar behavior in classes too?
Any explanation regarding the order of constructor and destructor calls is much appreciated.
Note: This is not homework. And, I have researched similar topics but nothing came up regarding this issue.
Undefined behavior
You're invoking undefined behavior by calling
foo
before the object is fully initialized. Quote from 12.6.2 in the C++ standard :In other words, this would be ok according to the standard :
And this will print
10
instead of the0
that you got (which could have been anything else, since that was undefined behavior).Initialization order
Setting aside this matter of undefined behavior, and to address your question, the order in which initialization happens is well-defined :
So, in your code, the initialization order is :
B
(B::b
),A
(A::a
),C
().As noted in the comments below though, changing this initialization order (by eg. using
struct C : A, B
instead ofstruct C : B, A
) would not however get rid of the undefined behavior. CallingA::foo
before theB
part is initialized remains undefined, even if theA
part is initialized.This is just another case of undefined behavior. For example, my system gives the following results.
Try this live demo which produces yet another distinct result (
C(10).bar()
produced 32764).foo()
can be called in this context, but it will be called beforeA
's constructor. This meansa
is initialized, which leads to reading an uninitialized variable, which leads to undefined behavior. This is similar to accessing a member before it's initialized. Consider the following example.a
is initialized tob
's value, thenb
is initialized. The problem is obvious,b
is uninitialized at the point where it's read to initializea
.