Constructor Initialization

M

Michael McKnerney

Hi,

It seems I can influence how a base class is initialized beyond the
'normal' manner and I was wondering if someone can tell me why this.
Here's my example.

class A
{
public:
A(int a=1, int b=1, int c=1) : _a(a), _b(b), _c(c) {}
void print()
{ cout << _a << "," << _b << "," << _c << endl; }
private:
int _a, _b, _c;
};

class B : public virtual A
{
public:
B(int a=1, int b=1, int c=1) : A(a, b, c) {}
};

class C : public virtual B
{
public:
C(int a=1, int b=1, int c=1)
:
B(a, b, c),
A(2,2,2) // I can explicitly initialize A here and override
the default values
{}
};

main()
{
C c;
c.print();
}


thanks,
Mike
 
R

Ron Natalie

Michael McKnerney said:
Hi,

It seems I can influence how a base class is initialized beyond the
'normal' manner and I was wondering if someone can tell me why this.

Because you have virtual base classes. The most derived class must initialize
all the virtual bases. The initialization of A specified in the B constructor has no
bearing on the creation of C objects.

You must provide an int return here (even in C these days).
 
D

Dhruv

Hi,

It seems I can influence how a base class is initialized beyond the
'normal' manner and I was wondering if someone can tell me why this.
Here's my example.
[snip]......


For virtual bases, the rule is different. The most derived class is
responsible for constructing the virtual base class, as opposed to its
immediate descendant in case of non-virtual bases.

HTH,
-Dhruv.
 
M

Michael McKnerney

Dhruv said:
Hi,

It seems I can influence how a base class is initialized beyond the
'normal' manner and I was wondering if someone can tell me why this.
Here's my example.
[snip]......

For virtual bases, the rule is different. The most derived class is
responsible for constructing the virtual base class, as opposed to its
immediate descendant in case of non-virtual bases.

But wait a minute, if I remove the explicit initialization of A in C's
constructor list, A will get initialized by B.
 
R

Ron Natalie

Michael McKnerney said:
But wait a minute, if I remove the explicit initialization of A in C's
constructor list, A will get initialized by B.

No it will not. If you remove the explicit initialization, it will invoke the
default constructor. If A didn't have a default constructor, then you'd
get an error.
 
J

Jim Fischer

Michael said:
Dhruv wrote:

Hi,

It seems I can influence how a base class is initialized beyond the
'normal' manner and I was wondering if someone can tell me why this.
Here's my example.

[snip]......

For virtual bases, the rule is different. The most derived class is
responsible for constructing the virtual base class, as opposed to its
immediate descendant in case of non-virtual bases.

But wait a minute, if I remove the explicit initialization of A in C's
constructor list, A will get initialized by B.

Nope. If you remove the explicit initialization of A in C's initializer
list and then invoke the class C ctor,

C(int a=1, int b=1, int c=1)
: B(a,b,c)
{ }

the sequence of events (IIRC) is this:

1) Program invokes the C ctor
2) The C ctor implicitly invokes A's default ctor
3) The C ctor invokes 'B(a,b,c)' as specified in the initializer list of
the class C ctor. The B ctor does not invoke the A ctor.

So the class C ctor -- not the B ctor -- is still the entity that
invokes the class A ctor.


FWIW, if you rewrite your code a bit, this sequence of events will (IMO)
be easier to observe.

<example>
<code>

#include <iostream>
using namespace std;

class A {
public:
A(int a=1, int b=1, int c=1)
: a_(a), b_(b), c_(c)
{ cout << "A(" << a << ',' << b << ',' << c << ")\n"; }

void print()
{ cout << a_ << ',' << b_ << ',' << c_ << '\n'; }

private:
int a_, b_, c_;
};

class B : public virtual A {
public:
B(int a=2, int b=2, int c=2)
: A(a, b, c)
{ cout << "B(" << a << ',' << b << ',' << c << ")\n"; }
};

class C : public virtual B {
public:
C(int a=3, int b=3, int c=3)
// : A(4,4,4), B(a,b,c)
: B(a,b,c)
{ }
};

int main()
{
C c;
c.print();
}

</code>

<output>
A(1,1,1)
B(3,3,3)
1,1,1
</output>

</example>


Some side notes:

1) DO NOT use variable names that start with a leading underscore, e.g.,
'_a'. These names (and some others) are reserved for the
implementation's use -- e.g., the standard library, compiler-specific
language extensions, the OS's API, etc. So if you want to use
underscores to denote variable names, place the underscore at the end of
the name, 'a_', and not at the beginning.

2) In this code sample, class A's ctor is *always* invoked before class
B's ctor. This sequence holds even if you try (futilely) to specify the
base class ctor invocation sequence as B first, then A, in the class C
ctor initializer list:

C() : B(), A() { }

IOW, the compiler ignores the B,A sequence specified above, and
implicitly implements C's initializer list as A,B, i.e.,

C() : A(), B() { }

[n.b. Most compilers are nice enough to warn you when they implicitly
reorder the initializer list's arguments like this.]
 

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

No members online now.

Forum statistics

Threads
473,755
Messages
2,569,536
Members
45,009
Latest member
GidgetGamb

Latest Threads

Top