Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C++ Multiple Virtual Inheritance vs. COM

The net is overflowing with explanations of the "dreaded diamond problem". So is StackOverflow. I think I understand that bit, but I fail to translate that knowledge into comprehending something similar yet different.

My question begins as a pure C++ question, but the answer might well branch over into MS-COM specifics. The general problem question goes:

class Base { /* pure virtual stuff */ };
class Der1 : Base /* Non-virtual! */ { /* pure virtual stuff */ };
class Der2 : Base /* Non-virtual! */ { /* pure virtual stuff */ };
class Join : virtual Der1, virtual Der2 { /* implementation stuff */ };
class Join2 : Join { /* more implementation stuff + overides */ };

This is not the classic diamond solution. Exactly what does "virtual" do here?

My real problem is trying to understand a discussion over at our friends' place at CodeProject. It involves a custom class for creating a transparent container for the Flash player.

I thought I would try this place for fun. It turns out that the following declaration crashes your app, with version 10 of the Flash player.

class FlashContainerWnd:   virtual public IOleClientSite,
                           virtual public IOleInPlaceSiteWindowless,
                           virtual public IOleInPlaceFrame,
                           virtual public IStorage

Debugging shows that when entering the function implementations (QueryInterface etc), from different callers, I get different "this"-pointer values for different calls. But removing "virtual" does the trick! No crashes, and same "this"-pointer.

I would like to clearly understand exactly what is going on. Thanks a lot.

Cheers Adam

like image 328
Adam Avatar asked Nov 18 '08 17:11

Adam


People also ask

What is virtual multiple inheritance?

Virtual inheritance is a C++ technique that ensures only one copy of a base class's member variables are inherited by grandchild derived classes.

What is alternative for multiple inheritance?

Interfaces provide an alternative to multiple inheritance. Java programming language does not support multiple inheritance. But interfaces provide a good solution.

Can multiple inheritance have virtual classes?

Virtual base classes are used in virtual inheritance in a way of preventing multiple “instances” of a given class appearing in an inheritance hierarchy when using multiple inheritances.

Why virtual classes are important in the case of multiple inheritance?

Instead, if classes B and C inherit virtually from class A, then objects of class D will contain only one set of the member variables from class A. As you probably guessed, this technique is useful when you have to deal with multiple inheritance and it's a way to solve the infamous diamond inheritance.


2 Answers

The virtual inheritance in the first example don't do anything. I would wager that they compile to the same code if they were removed.

The virtually inherited class just flag the compiler that it should merge later versions of Der1 or Der2. Since only one of each appears in the inheritance tree nothing is done. The virtuals have no effect on Base.

auto p = new Join2;
static_cast<Base*>(static_cast<Der1*>(p)) !=
      static_cast<Base*>(static_cast<Der2*>(p))

The virtual inheritance only effects the next inherited class, and only for instances that have been delcared virtual. This is backward from what you would expect, but it's a limitation on the way classes are compiled.

class A {};
class B : virtual public A {};
class C : virtual public A {};
class D : public A {};
class E : virtual public A, public B, public C, public D {};
class F : public A, public B, public C, public D {};

F::A != F::B::A or F::C::A or F::D::A
F::B::A == F::C::A
F::D::A != F::B::A or F::C::A or F::A

E::B::A == E::C::A == E::A
E::D::A != E::B::A or E::C::A or E::D::A

One of the reasons A must be marked virtual in C and B instead of E or F is that C and B need to know not to call A's constructor. Normally they would have initialize each of their copies. When they are involved in diamond inheritance they wont. But you cant recompile B and C to not construct A. That means C and B have to know ahead of time to create constructor code where A's constructor is not called.

like image 151
deft_code Avatar answered Oct 06 '22 01:10

deft_code


I think the issue with your COM example is that by adding the virtual keyword you are saying that all the IOle* interfaces share a common IUnknown implementation. In order to implement this the compiler has to create multiple v-tables, hence you different 'this' values depending on the derived class it came down.

COM requires that when you call IQueryInterface on an object for IUnknown that ALL interfaces exposed by the object return the same IUnknown ... which this implementation clearly breaks.

Without the virtual inheritance each IOle* nominally has its own IUnknown implementation. However, since IUnknown is an abstract class, and doesn't have any storage the compiler, and all the IUnknown implementations come from FlashContainerWnd there is only a single implementation.

(OK, so that last bit sounds weak ... perhaps someone with a better grasp of the language rules can explain it more clearly)

like image 40
Rob Walker Avatar answered Oct 06 '22 01:10

Rob Walker