order of constructor execution...

R

Rahul

Hi Everyone,

I was trying to understand the constructor order execution and i
tried the following example,

#include <stdio.h>

class A
{
public: A()
{
printf("A\n");
}
};

class D
{
public : D()
{
printf("D\n");
}
D(int i)
{
printf("D is %d\n",i);
}
};

class E
{
public: E()
{
printf("E\n");
}
E(int i)
{
printf("E is %d\n",i);
}
};

class C : virtual public E, virtual public D
{
public: C()
{
printf("C\n");
}
C(int a) : D(a), E(a)
{
printf("C is %d\n",a);
}
};

class B : public A, public C
{
C c_obj;
public: B() : C(5), c_obj(10)
{
printf("B\n");
}
};

int main()
{
B obj;
return (0);
}

I get the following output,

E // virtual base (left to right)
D // virtual base (left to right)
A // base class
C is 5 // base class
E is 10 // member object
D is 10 // member object
C is 10 // member object
B // constructor


Yes, the virtual base class constructors (default) are invoked first
for obj as expected.
However, for the member object c_obj, the virtual base class
constructors aren't invoked ( i mean the default constructor of E and
D) instead the second version ( accepting an integer ) is called?

Is there any special rule for member objects as far as initialization
of virtual base classes are concerned?
 
C

Craig Scott

Hi Everyone,

I was trying to understand the constructor order execution and i
tried the following example,

#include <stdio.h>

class A
{
public: A()
{
printf("A\n");
}

};

class D
{
public : D()
{
printf("D\n");
}
D(int i)
{
printf("D is %d\n",i);
}

};

class E
{
public: E()
{
printf("E\n");
}
E(int i)
{
printf("E is %d\n",i);
}

};

class C : virtual public E, virtual public D
{
public: C()
{
printf("C\n");
}
C(int a) : D(a), E(a)
{
printf("C is %d\n",a);
}

};

class B : public A, public C
{
C c_obj;
public: B() : C(5), c_obj(10)
{
printf("B\n");
}

};

int main()
{
B obj;
return (0);

}

I get the following output,

E // virtual base (left to right)
D // virtual base (left to right)
A // base class
C is 5 // base class
E is 10 // member object
D is 10 // member object
C is 10 // member object
B // constructor

Yes, the virtual base class constructors (default) are invoked first
for obj as expected.
However, for the member object c_obj, the virtual base class
constructors aren't invoked ( i mean the default constructor of E and
D) instead the second version ( accepting an integer ) is called?

Why should the default constructors be called? You specify the non-
default constructors in your initialization list, so those will be
used. As you correctly observe, virtual base classes are initialized
before all other base classes and this is done in the order they are
listed in the class definition.
Is there any special rule for member objects as far as initialization
of virtual base classes are concerned?

The C++ standard explicitly states what is happening in your example
in 12.6.2/6:

"All sub-objects representing virtual base classes are initialized by
the constructor of the most derived class (1.8). If the constructor of
the most derived class does not specify a mem-initializer for a
virtual base class V, then V's default constructor is called to
initialize the virtual base class subobject."

And you *do* supply a mem-initializer for the virtual bases of C for
the C(int) constructor, so those are used when you create your B
object in main() because B uses the C(int) constructor for its c_obj
data member.
 
R

Rahul

Why should the default constructors be called? You specify the non-
default constructors in your initialization list, so those will be
used. As you correctly observe, virtual base classes are initialized
before all other base classes and this is done in the order they are
listed in the class definition.


The C++ standard explicitly states what is happening in your example
in 12.6.2/6:

"All sub-objects representing virtual base classes are initialized by
the constructor of the most derived class (1.8). If the constructor of
the most derived class does not specify a mem-initializer for a
virtual base class V, then V's default constructor is called to
initialize the virtual base class subobject."

And you *do* supply a mem-initializer for the virtual bases of C for
the C(int) constructor, so those are used when you create your B
object in main() because B uses the C(int) constructor for its c_obj
data member.

--

Ok, so this is standard for member objects but not for base class
constructors, like C(5)
doesn't invoke E(5) and D(5) instead calls the default constructor of
D and E... but why is this so?
 
V

Victor Bazarov

Rahul said:
Ok, so this is standard for member objects but not for base class
constructors, like C(5)
doesn't invoke E(5) and D(5) instead calls the default constructor of
D and E... but why is this so?

Why should it be NOT so? How should the compiler know that you
intend to pass the 5 along to the base classes and not 5*rand() ?

V
 
R

Rahul

Why should it be NOT so? How should the compiler know that you
intend to pass the 5 along to the base classes and not 5*rand() ?

V

well, thats where i'm confused with the initial output of the program
which prints

E
D

and not

E is 5
D is 5

I hope my doubt over here is clear...
 
V

Victor Bazarov

Rahul said:
well, thats where i'm confused with the initial output of the program
which prints

E
D

and not

E is 5
D is 5

I hope my doubt over here is clear...

I think you're missing the simple fact that virtual base class subobjects
are constructed by the most derived class' constructor. If you don't put
virtual base class initialisers in _that_ initialiser list, the default
constructor is used. Is there anything unclear about this particular rule?

V
 
R

Rahul

I think you're missing the simple fact that virtual base class subobjects
are constructed by the most derived class' constructor. If you don't put
virtual base class initialisers in _that_ initialiser list, the default
constructor is used. Is there anything unclear about this particular rule?

V

I'm afraid that doesn't help, could you illustrate with a simple
example...
And moreover, i didn't get the reason as why virtual bases have to be
initialized first...
may be i need to know that...
 
V

Victor Bazarov

Rahul said:
[..]
I think you're missing the simple fact that virtual base class
subobjects are constructed by the most derived class' constructor.
If you don't put virtual base class initialisers in _that_
initialiser list, the default constructor is used. Is there
anything unclear about this particular rule?

V

I'm afraid that doesn't help, could you illustrate with a simple
example...
And moreover, i didn't get the reason as why virtual bases have to be
initialized first...
may be i need to know that...

Here is what you are seeing:

struct A {
int a;
A() : a(42) {}
A(int a_) : a(a_) {}
};

struct B : virtual A {
B() : A(666) {} // initialises 'A' using A::A(int)
};

struct C : B {
C() {} // initialises 'A' using A::A()
};

#include <iostream>
#include <ostream>
int main() {
B b;
C c;
std::cout << "b.a = " << b.a << std::endl;
std::cout << "c.a = " << c.a << std::endl;
}

The reason the virtual base class is constructed in the most derived
object's initialiser list is because with every object there will be
only one instance of it if more than one base class derives from it
virtually. Here is another example:


struct A {
int a;
A() : a(42) {}
A(int a_) : a(a_) {}
};

struct B1 : virtual A {
B1() : A(666) {} // initialises 'A' using A::A(int)
};

struct B2 : virtual A {
B2() : A(777) {} // initialises 'A' using A::A(int)
};

struct C : B1, B2 {
C() {} // initialises 'A' using A::A()
};

#include <iostream>
#include <ostream>
int main() {
B1 b1;
B2 b2;
C c;
std::cout << "b1.a = " << b1.a << std::endl;
std::cout << "b2.a = " << b2.a << std::endl;
std::cout << "c.a = " << c.a << std::endl;
}

Since in a 'C' instance there is only one instance of 'A', that
subobject has to be constructed only once during the construction
of a single 'C' object. Both 'B1' and 'B2' (and possibly more)
initialise "their" 'A' subobjects according to how they want.
The compiler cannot pick which initialiser from those provided
by 'Bx' classes to use. The only logical place, then, is to
require that the _common_ 'A' is initialised by the 'C's c-tor.

V
 
J

Joe Greer

I'm afraid that doesn't help, could you illustrate with a simple
example...
And moreover, i didn't get the reason as why virtual bases have to be
initialized first...
may be i need to know that...

In C++, the rule is that until a constructor has successfully completed,
you don't have an object. This is true during construction as well. If
a derived class is going to be able to make use of methods or variables
from a parent class, then they must have been constructed first. Thus,
the construction order always goes from parent to child and the
destruction order always goes from child to parent.

By default, the default constructor is used when creating any object.
If you want a different constructor to be used, you must specify the
constructor with the parameters you want to use. For example:

E e;

would initialize e with the default constructor whereas:

E e(5);

would invoke the constructor which takes an int (ignoring implicit casts
for now).

For simple objects, you specify the constructor by giving a parameter
list to the variable name. With constructors, you specify the
constructor by giving the class name followed by the parameter list in
the deriveds constructor. For example:

class B {
public:
B(int b) : m_B(b) {}
private:
int m_B;
};

class D : public B
{
public:
D(int d) : B(d) {}
};

Hope that helps some,

joe
 
R

Rahul

Rahul said:
[..]
I think you're missing the simple fact that virtual base class
subobjects are constructed by the most derived class' constructor.
If you don't put virtual base class initialisers in _that_
initialiser list, the default constructor is used. Is there
anything unclear about this particular rule?
V
I'm afraid that doesn't help, could you illustrate with a simple
example...
And moreover, i didn't get the reason as why virtual bases have to be
initialized first...
may be i need to know that...

Here is what you are seeing:

struct A {
int a;
A() : a(42) {}
A(int a_) : a(a_) {}
};

struct B : virtual A {
B() : A(666) {} // initialises 'A' using A::A(int)
};

struct C : B {
C() {} // initialises 'A' using A::A()
};

#include <iostream>
#include <ostream>
int main() {
B b;
C c;
std::cout << "b.a = " << b.a << std::endl;
std::cout << "c.a = " << c.a << std::endl;
}

The reason the virtual base class is constructed in the most derived
object's initialiser list is because with every object there will be
only one instance of it if more than one base class derives from it
virtually. Here is another example:

struct A {
int a;
A() : a(42) {}
A(int a_) : a(a_) {}
};

struct B1 : virtual A {
B1() : A(666) {} // initialises 'A' using A::A(int)
};

struct B2 : virtual A {
B2() : A(777) {} // initialises 'A' using A::A(int)
};

struct C : B1, B2 {
C() {} // initialises 'A' using A::A()
};

#include <iostream>
#include <ostream>
int main() {
B1 b1;
B2 b2;
C c;
std::cout << "b1.a = " << b1.a << std::endl;
std::cout << "b2.a = " << b2.a << std::endl;
std::cout << "c.a = " << c.a << std::endl;
}

Since in a 'C' instance there is only one instance of 'A', that
subobject has to be constructed only once during the construction
of a single 'C' object. Both 'B1' and 'B2' (and possibly more)
initialise "their" 'A' subobjects according to how they want.
The compiler cannot pick which initialiser from those provided
by 'Bx' classes to use. The only logical place, then, is to
require that the _common_ 'A' is initialised by the 'C's c-tor.

V

Thanks victor for the examples, thats puts things in perspective...
 
J

James Kanze

[...]
And moreover, i didn't get the reason as why virtual bases
have to be initialized first...

Who should initialize it?

In your precise example, one could argue that C should, since
it's not explicitly known any higher. But what happens then if
you derived from B:

class F : public B, public virtual E

? One could imagine a rule in which the compiler constructed
the inheritance graph, and then initialized the virtual base in
the highest node which included all possible paths to it, but
this would be considerably more complicated, both to implement
and to understand, since it would result in different virtual
bases being initialized in different classes in the hierarchy.
(In the above example, for example, E would be initialized by F,
but D would still be initialized by C. And what does it buy us?
In practice, virtual base classes are almost always empty, and
only have default constructors (since they have no data to
initialize).
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,744
Messages
2,569,483
Members
44,903
Latest member
orderPeak8CBDGummies

Latest Threads

Top