Multiple inheritance: Wrong constructors being called?

A

Adam Nielsen

Hi everyone,

I'm having some trouble getting the correct chain of constructors to be called when creating an object at the bottom of a hierarchy. Have a look at the code below - the inheritance goes like this:

Shape
|
+-- Ellipse
|
+-- Circle

When I create a new Circle, it calls the Ellipse's constructor, which in turn calls the Shape's constructor. The problem is that I'm passing parameters to the constructors, and the Ellipse's constructor passes these parameters on to the Shape's constructor - but these parameters are ignored and the Shape's default constructor is called instead of the one taking parameters!

What am I doing wrong???

----------------------------------------------
#include <iostream>

class Shape
{
public:
Shape(void)
{
std::cout << "Default shape constructor, should be unused" << std::endl;
}

Shape(int width, int height)
{
std::cout << "Creating a new shape with size " << width << "x" << height << std::endl;
}
};

class Ellipse: virtual public Shape
{
public:
Ellipse(int width, int height):
Shape(width, height)
{
std::cout << "In Ellipse constructor, a shape with size should have already been created above" << std::endl;
}
};

class Circle: virtual public Ellipse
{
public:
Circle(int width, int height):
Ellipse(width, height)
{
std::cout << "In Circle constructor, Ellipse should already have been created above" << std::endl;
}
};

int main(void)
{
Circle *c = new Circle(10, 20);
delete c;
return 0;
}

----------------------------------------------

$ g++ -o test test.cpp && ./test

Default shape constructor, should be unused
In Ellipse constructor, a shape with size should have already been created above
In Circle constructor, Ellipse should already have been created above
 
J

John Harrison

Adam said:
Hi everyone,

I'm having some trouble getting the correct chain of constructors to be called when creating an object at the bottom of a hierarchy. Have a look at the code below - the inheritance goes like this:

Shape
|
+-- Ellipse
|
+-- Circle

When I create a new Circle, it calls the Ellipse's constructor, which in turn calls the Shape's constructor. The problem is that I'm passing parameters to the constructors, and the Ellipse's constructor passes these parameters on to the Shape's constructor - but these parameters are ignored and the Shape's default constructor is called instead of the one taking parameters!

What am I doing wrong???

Using virtual inheritance. Drop the virtual and everything will work as
you expect.

john
 
A

Adam Nielsen

Using virtual inheritance. Drop the virtual and everything will work
as you expect.

Ah yes, that worked. I think I'll need to re-think the virtual
inheritance in the rest of my code... :-/

Thanks for your help!

Cheers,
Adam.
 
J

John Harrison

Adam said:
Hi everyone,

I'm having some trouble getting the correct chain of constructors to be called when creating an object at the bottom of a hierarchy. Have a look at the code below - the inheritance goes like this:

Shape
|
+-- Ellipse
|
+-- Circle

When I create a new Circle, it calls the Ellipse's constructor, which in turn calls the Shape's constructor. The problem is that I'm passing parameters to the constructors, and the Ellipse's constructor passes these parameters on to the Shape's constructor - but these parameters are ignored and the Shape's default constructor is called instead of the one taking parameters!

What am I doing wrong???

BTW making a circle inherit from an ellipse is a classic mistake in OO
programming, see here

http://en.wikipedia.org/wiki/Circle-ellipse_problem

Speculating from the code you've posted I would guess that you've
misunderstood the concept of multiple inheritance. Multiple inheritance
means a class having more than one *direct* base class. And there are
situations using multiple inheritance when you also need to use virtual
inheritance, and yes it does change the rules for how constructors are
called.

However multiple inheritance does not mean having an inheritance
hierarchy with a depth of greater than one. That is what the code you
posted has, but it is still simple single inheritance.

john
 
D

Doug

Hi everyone,

I'm having some trouble getting the correct chain of constructors to be called when creating an object at the bottom of a hierarchy. Have a look at the code below - the inheritance goes like this:

Shape
|
+-- Ellipse
|
+-- Circle

When I create a new Circle, it calls the Ellipse's constructor, which in turn calls the Shape's constructor. The problem is that I'm passing parameters to the constructors, and the Ellipse's constructor passes these parameters on to the Shape's constructor - but these parameters are ignored and the Shape's default constructor is called instead of the one taking parameters!

What am I doing wrong???

----------------------------------------------
#include <iostream>

class Shape
{
public:
Shape(void)
{
std::cout << "Default shape constructor, should be unused" << std::endl;
}

Shape(int width, int height)
{
std::cout << "Creating a new shape with size " << width << "x" << height << std::endl;
}

};

class Ellipse: virtual public Shape
{
public:
Ellipse(int width, int height):
Shape(width, height)
{
std::cout << "In Ellipse constructor, a shape with size should have already been created above" << std::endl;
}

};

class Circle: virtual public Ellipse
{
public:
Circle(int width, int height):
Ellipse(width, height)
{
std::cout << "In Circle constructor, Ellipse should already have been created above" << std::endl;
}

};

int main(void)
{
Circle *c = new Circle(10, 20);
delete c;
return 0;

}

----------------------------------------------

$ g++ -o test test.cpp && ./test

Default shape constructor, should be unused
In Ellipse constructor, a shape with size should have already been created above
In Circle constructor, Ellipse should already have been created above

Hiya,

In C++, when you create an instance of a class, virtual base class
constructors are called first. If you don't have an explicit call to
a virtual base class ctor, then the compiler will call it under the
covers *and will use the default [no parameter] constructor*. If you
want it to call a non-default ctor, you must make an explicit call to
the ctor in the initializer list.

So, your Circle has two virtual base classes - Ellipse and Shape. The
standard says that these base classes should be constructed in left-to-
right order depth first search. In your case, that means the compiler
will want to initialize Shape first, then Ellipse. The compiler sees
that you haven't specified an explicit initializer call to a Shape
ctor, so it adds in a call to the default ctor for you.

So, if you add the following to your Circle ctor initializer list:

public:
Circle(int width, int height): Ellipse(width, height)
{

then you end up with what you wanted.

Hope that helps,
Doug
 
A

Adam Nielsen

Hi John,
BTW making a circle inherit from an ellipse is a classic mistake in OO
programming
Speculating from the code you've posted I would guess that you've
misunderstood the concept of multiple inheritance.

This code was just an example to illustrate the problem - my real code
is different, but you may have a point. I was using each subclass to
represent a more specific case of each parent (in the same way that an
Ellipse is a more specific version of a Shape.)

This may not be the best way of doing things, but in my particular case
it's certainly the easiest :)
Multiple inheritance means a class having more than one *direct* base
class. And there are situations using multiple inheritance when you
also need to use virtual inheritance, and yes it does change the rules
for how constructors are called. However multiple inheritance does
not mean having an inheritance hierarchy with a depth of greater than
one. That is what the code you posted has, but it is still simple
single inheritance.

Eventually later on in the program I do use multiple inheritance
properly (e.g. a class may inherit both an Ellipse and a Rectangle,
which would give it two copies of the Shape base class) however having
said that these particular classes will never be multiply inherited - so
there was no need for virtual inheritance here after all (but when
you're still not 100% familiar with the concepts, a rule of thumb like
making all inheritance virtual is really tempting!)

Cheers,
Adam.
 
A

Adam Nielsen

Hi Doug,
In C++, when you create an instance of a class, virtual base class
constructors are called first.

Yes, I now realise why that happens.
So, if you add the following to your Circle ctor initializer list:

public:
Circle(int width, int height):
Ellipse(width, height)
{

then you end up with what you wanted.

I had thought about something like that when the problem was first
pointed out, but I was somewhat reluctant to do it this way - the Circle
isn't really supposed to be aware of the Shape, it's only supposed to
know about the workings of its direct parent class, the Ellipse.

Thanks for your suggestion though!

Cheers,
Adam.
 
D

Doug

Hi Doug,


Yes, I now realise why that happens.




I had thought about something like that when the problem was first
pointed out, but I was somewhat reluctant to do it this way - the Circle
isn't really supposed to be aware of the Shape, it's only supposed to
know about the workings of its direct parent class, the Ellipse.

Thanks for your suggestion though!

Cheers,
Adam.

Hi Adam,

Totally fair play, mate. I wouldn't have done it either.

Doug
 

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,482
Members
44,901
Latest member
Noble71S45

Latest Threads

Top