Intel® C++ Compiler
Community support and assistance for creating C++ code that runs on platforms based on Intel® processors.

Virtual Inheritance - Need Help

stephen-smolley
459 Views
Hey there,

I'm having a problem with class inheritance.

Most of you probably know what Diamond Inheritance is:

Non-Diamond Inheritance:
class cBase { ... };
class cMiddle1 : public cBase { ... };
class cMiddle2 : public cBase { ... };
class cTop : public cMiddle1, public cMiddle2 { ... };

cTop top; // Contains 2 instances of cBase, 1 of cMiddle1, 1 of cMiddle2, and 1 of cTop.
// cTop::cMiddle1::cBase != cTop::cMiddle2::cBase

Diamond Inheritance:
class cBase { ... };
class cMiddle1 : public
virtual cBase { ... };
class cMiddle2 : public virtual cBase { ... };
class cTop : public cMiddle1, public cMiddle2 { ... };

cTop top; // Contains ONE instance of cBase, 1 of cMiddle1, 1 of cMiddle2, and 1 of cTop.

// cTop::cMiddle1::cBase == cTop::cMiddle2::cBase
_______________________________________________________________________________

I've hit a roadblock in my project when I tried to have virtual inheritance as shown here:
class c1 : public virtual CWnd { ... };
class c2 : public CFormView, public c1 {
... DECLARE_DYNCREATE(c2, CFormView) ... };

Inside a function belonging to c2, I used the Watch in the debugger to test whether or not the virtual inheritance was working. It wasn't:

this = 0x003b81d0
(CWnd*)(CView*)(CScrollView*)(CFormView*)this = 0x003b81d0
(CWnd*)(c1*)this = 0x003b829c

The problem is these pointers are not the same, and therefore, there are two instances of CWnd created with each creation of a c2. There should only be one instance of CWnd.

If you know how to fix this, I'd be happy to hear your solutions!

This issue is not specific to x64 or x86, I don't think, but I think this would be the best place to post. If not, please let me know where it'd be better to ask!

Thanks,

-Steve
0 Kudos
2 Replies
Roman_D_Intel
Employee
459 Views
Hi Steve,

I suggest you to ask this question on a C++ language forum. You can try Visual C++ Language Forum, for example: http://social.msdn.microsoft.com/Forums/en-US/vclanguage/threads

Best,
Roman
0 Kudos
plus2plus
Beginner
459 Views
Taking examples from "The C++ Programming Language" (3rd or special editions) by Bjarne Stroustrup. In this first example having 2 copies of a base sub-object in the final object type is reasonable:

struct Link { Link * next; };

class Task : public Link
{
// Link used to maintain list of tasks (the scheduler list)
};

class Displayed : public Link
{
// Link used to maintain list of all Displayed objects
// (the display list)
};

Now we define a Satellite class that is both a task and is displayed:

class Satellite : public Task, public Displayed
{
//...
};

This is fine - two separate Link objects are used to maintain a Satellite on two separate lists. Stroustrup warns to be careful of ambiguities however - referring to a Link within a Satellite will cause problems - which Link did you mean? So you have to refer to Task or Displayed instead to make it clear which bases' Link you are interested in, for example:

void mess_with_links( Satellite * p)
{
p->next = 0; // Error: ambiguous - which Link's next?
p->Link::next = 0; // Error: ambiguous - which Link?
p->Task::next = 0; // OK
p->Displayed::next = 0; // OK
}

In the following example a case where have two base sub-objects starts out being a reasonable state of affairs, but after some modifications maintaining the correct behaviour becomes cumbersome.

So, here is the initial version of the class hierarchy:

class Storable
{
public:
virtual const char * get_file() = 0;
virtual void read() = 0;
virtual void write() = 0;
virtual ~Storable() {}
// ...
};

class Receiver : public Storable
{
public:
void write();
// ...
};

class Transmitter : public Storable
{
public:
void write();
// ...
};

class Radio : public Receiver, public Transmitter
{
public:
const char * get_file();
void read();
void write();
// ...
};

Here Radio::write, for example, can call its two base implementations then do its own stuff:

void Radio::write()
{
Receiver::write();
Transmitter::write();
// Radio specific write stuff...
};

Again this works because an object can safely and conveniently have more than one Storable sub-object within its make up.

However if, as may well be the case, Storable is updates in some fashion - such as storing the name of the file in the Storable base class, as below:

class Storable
{
public:
Storable( const char * file_name );
virtual const char * get_file() = 0;
virtual void read() = 0;
virtual void write() = 0;
virtual ~Storable() {}
// ...
private:
const * char store_file_name;

Storable( Storable const & );
Storable & operator=( Storable const & );
};

Stroustrup points out that this simple little change to Storable means that the design of the Radio class needs to change so that we only have one Storable sub-object in Radio objects rather than the two we had previously to prevent it becoming "unnecessarily hard" to avoid storing multiple copies of the object.

To do this we make Storable a virtual base class of Receiver and Transmitter (note the use of virtual after public in the base class list):

class Receiver : public virtual Storable
{
public:
void write();
// ...
};

class Transmitter : public virtual Storable
{
public:
void write();
// ...
};

class Radio : public Receiver, public Transmitter
{
public:
void write();
// ...
};

This forces Radio to only have one Storable sub-object that is shared by each of the Transmitter and Receiver intermediate base classes.

So rather than looking something like the next diagram which shows the original version of a Radio object without virtual bases (view the using a fixed-space font or the spacing will not work...):

Storable Storable
^ ^
Receiver Transmitter
^ ^
Radio

(sorry but diagrams in plain text are difficult !)

We have with the virtual bases, meaning we use virtual inheritance:

Storable
^ ^
Receiver Transmitter
^ ^
Radio

As you can see the second version of a Radio object's layout differs from the first in that it has only a single Storable sub-object in its make up. This is what virtual inheritance is about.
0 Kudos
Reply