C++ multiple inheritance [was Re: [GD-General] Eiffel]
Brought to you by:
vexxed72
From: Thatcher U. <tu...@tu...> - 2002-01-03 04:47:17
|
On Dec 27, 2001 at 03:46 -0800, Jesse Jones wrote: > At 4:56 PM -0500 12/27/01, Thatcher Ulrich wrote: > >On Dec 22, 2001 at 04:30 -0800, Jesse Jones wrote: > >> > >> Unfortunately MI has a bad reputation in C++. Even people who should > >> know better like Scot Myers rag on it. But essentially all of the > >> problems with MI in C++ are because of virtual base classes. So how > >> do you avoid virtual base classes? By avoiding diamond shaped > >> inheritance hierarchies. > >> > >> One way to do this is by using mixin classes. You have your main-line > >> classes like Widget or Monster or whatever and then you have mixin > >> classes like ReferenceCountedMixin or ObserverMixin or whatever. > >> Mixins only descend from other mixins so you never wind up with the > >> diamond of death. > > > >I've been pondering this. I've always depended on virtual inheritance > >for effective mixins. For example, how do you implement smart > >pointers to mixins of ref-counted classes, without virtual > >inheritance? > > I assume you're talking about a smart pointer class that requires > invasive changes to the object it's pointing at. For example, a smart > pointer class that maintains ref counts and requires that the object > have a common reference counted implementation. Yup, that's what I meant. > In general I think it's better to try to come up with a non-invasive > design. I'm curious to know more; can you explain, or point me at a reference? > However I have dealt with problems like this. For example, I used to > have an Invariant class that allowed for DbC-style invariant checks. > The class had a virtual Invariant method and a counter that was > incremented when entering a public method and decremented when > exiting a public method. But mixins still wanted to call the > invariant so what I did was dynamic_cast the mixin this pointer to an > Invariant* and call the Invariant method if the result wasn't NULL. I've been pondering this. I think dynamic_cast has a couple of problems for this usage. For one thing, it's slower than a virtual base class lookup. More importantly, dynamic_cast<C*>(p) returns null if *p has more than one C in its inheritance hierarchy! This is explained in TC++PL, however the text mistakenly says that this problem only occurs with virtual inheritance. For example: B B | | C D A \|/ E E inherits from B twice. dynamic_cast<B*>((A*) ptr to E) returns 0! I coded this up, and both GCC and MSVC agree. I checked with Stroustrup (the person, not the book), and he agrees too; he's going to correct the text in TC++PL. On the other hand, if you *always* use virtual inheritance, the ambiguity *never* occurs; the above graph looks like: B |\ C D A \|/ E And presuming that B is your base class with the reference count, the compiler will force you to derive A from B as well, if you try to make a smart pointer to A. I realize that you're advocating a hierarchy that looks like this: B | C D A \|/ E But I don't think there's a good way to enforce that. -- Thatcher Ulrich <tu...@tu...> http://tulrich.com |