Sign in to follow this  
Gumgo

C++ multiple inheritance

Recommended Posts

Gumgo    968
I'm using multiple inheritance for some stuff, and I'm wondering if anyone could explain how it is generally implemented. I don't quite get how some things are possible. Suppose I have the following:
class BaseA {
public:
	int a;
};

class BaseB {
public:
	int b;
};

class DerivedAB : public BaseA, public BaseB {
public:
	void setAB() {
		a = 10;
		b = 20;
	}
};

First of all, in memory, what is the layout of DerivedAB? Is it BaseA followed by BaseB? I did the following test:
DerivedAB * derived = new DerivedAB();
derived->setAB();

BaseA * aPtr = derived;
BaseB * bPtr = derived;

std::cout << "Value of a from aPtr: " << aPtr->a << '\n';
std::cout << "Value of b from bPtr: " << bPtr->b << '\n';
std::cout << "Location of derived: " << derived << '\n';
std::cout << "Location of aPtr: " << aPtr << '\n';
std::cout << "Location of bPtr: " << bPtr << '\n';

DerivedAB * derivedPtr = (DerivedAB*)bPtr;
std::cout << "Value of b from derivedPtr: " << derivedPtr->b << '\n';

delete bPtr;

I create a new DerivedAB and then point to it from a pointer of each base class. The output I get (which is expected) is as follows:
Value of a from aPtr: 10
Value of b from bPtr: 20
Location of derived: 00035F98
Location of aPtr: 00035F98
Location of bPtr: 00035F9C
Value of b from derivedPtr: 20
When (implicitly) casting from DerivedAB* to BaseB*, the location of bPtr was offset by 4 from the location of derived. How does delete work with this? In my test, I new a DerivedAB and I delete a BaseB. How is it determined that the object I should be deleting is actually 4 memory locations back? Basically, some info on how this is implemented would be useful because I don't want to mess something up because I don't understand it. Thanks.

Share this post


Link to post
Share on other sites
Hodgman    51336
Quote:
delete bPtr;
Ignoring multiple inheritance, this is already doing the wrong thing. If you want to be able to delete derived types from base pointers, then the base needs to have a virtual destructor.
e.g.
class BaseB {
public:
virtual ~BaseB() {}
int b;
};
These links should help:
[11] Destructors
[20.7] When should my destructor be virtual?
[25] Inheritance — multiple and virtual inheritance

Share this post


Link to post
Share on other sites
rip-off    10979
The memory layout for non-POD types is not defined in the standard. Then again, unless you are doing something really strange there is no reason your code should depend on the memory layout.

It is enough to say that the compiler should have a consistent model and that it will figure out how to do the correct thing in all situations required of it. As long as your code doesn't invoke undefined behaviour (such as deleting a type without using the correct compile type time, when such a type lacks a virtual destructor).

Now, for some more practical information. Typically, mutliple inheritance is implemented by having each object sequentially added to the last in memory, possibly with padding. Each object may have mutliple vtable pointers (again, assuming the most common implementation of dynamic dispatch). A pointer to a superclass will typically point to the offset in the derived class. This is one of the reasons why reinterpret_cast<> is dangerous, it will not take such offsets into consideration. I would strongly recommend using static_cast or dynamic_cast rather than C style casts.

Finally, inheritance is often overused where composition could be used instead. There are alternatives to multiple inheritance, and you should strongly consider them before taking the more complex solution.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this