That same reader from the previous posting now writes the following, where he quotes me and then makes his comment [this is great, by the way. this is much more engaging than the technical writing]:
“There are some significant implementation and performance problems with multiple inheritance – particularly virtual base classes which contain data members”
That’s up to the user to decide to opt for this ‘performance hit’ (which is not as significant as you make it). There are also serious performance issues related to datasets or to O/R mapping. You _don’t have to_ use MI to write software, however in a SI world, things get complicated in several situations. I wrote a plea for MI in .NET here: http://weblogs.asp.net/fbouma/archive/2004/01/04/47476.aspx.
Well, actually, the performance hit is quite significant. I base that on (a) supporting Stroustrup’s initial implementation under cfront for the 2.1 and 3.0 Release of the compiler, (b) implementing it on my own in an experimental compiler within the Grail project within Bell Laboratories headed up by Stroustrup, and (c) by doing a study of the implementations and costs in my book, Inside the C++ Object Model. For example, if the user writes
base *pb = pd;
if base is a virtual base class of the pd object, this requires to be carried out during run-time. If it is a global object, this requires the generation of a static initialization method that has to be executed before the beginning of main(). If this form occurs in enough modules, this can cause significant page faults at start-up, which is a significant blow to the application. The worst thing, however, is that the user can’t see the overhead in the statement since it looks trivial.
It is always alarming when a person counters a statement by saying “it is not as significant as you make it” since it now becomes a he said, she said kind of dialog, which of course cannot be resolved. So, I will leave it at that.
Besides that, C++ as a standard supports MI. You don’t support MI on .NET’s version of C++. Therefore C++ is severily crippled. You can give 10,000 excuses for that decision, that’s not the point. The point is: it’s not C++ anymore because you removed a CORNERSTONE of C++ from the language: if you want to use that feature, you have to go unmanaged. Now, isn’t the future going towards a managed world? So I don’t have a choice anymore?
Well, the first part of this assertion is true. We don’t support MI on C++/CLI. [The you here is a bit inflated. My influence on the language is mostly by reputation.] It will have its own standard separate from that of ISO C++, and there are a number of differences between the two languages.
The reader then says,
Therefore, C++ is severily crippled.
That therefore is not earned, although it has the pattern of a logical imperative. I have never used multiple inheritance, although, as I stated earlier, I have supported one implementation, and implemented a second. Tom Cargill has a long history of writing against the need for MI. Grady Booch has called it a parachute – something that might save one’s life in an emergency, but not something one uses every day. When I quoted that at an MI workshop at ECOOP, the developer of the Beta language shouted Hack, and everyone broke out laughing. I think that is my response to this statement of the reader.
Multiple inheritance is not a cornerstone of the C++ language. It was introduced into the language in Release 2.0, in part because Brad Cox, the inventor of Objective C, said it couldn’t be done. Stroustrup has subsequently said that he wished he had done templates before MI. If we are going to simply make assertions, then my assertion is, Multiple Inheritance is a corner of C++, but hardly a stone’s throw from a dark corner at that. [This is called rhetoric J]
In any case, clearly the reader is angry. It reminds me of an interview I read with Jacobson, the director of the Lord of the Rings trilogy. The woman asks, and what about this character? I really missed him. And he shrugs and asks, so you have been angry for two years? And then goes on and explains his reasoning. It also reminds me of a letter James Joyce responded to from a reader who complained at the lack of traditional characters in Ulysses. He responded that every book has its own inner logic that dictates its form, and that if you come to the book bringing your logic, there will be noise and frustration. Actually he didn’t quite say it that way. [And I mean noise here in the physics sense.]
The reader, having squashed me on the one quote, then goes on to quote me again – it is an interesting aspect of a Blog that the quote is so fresh in my mind. Usually, I get a 2 to 10 year quote thunked across my puzzled brow.
“Interfaces strike me as a potentially superior design; however, I don’t have actual experience with their use. I’d like to see people gain some experience with interfaces before they claim a superiority with MI”
You don’t understand Interfaces clearly. You have two types of MI: multiple type inheritance and multiple implementation inheritance. .NET only supports the former, by offering the feature of multiple inheritance via interfaces. (Interfaces do support MI in .NET). This however is a ‘hack’, because although you have multiple type inheritance, you still can’t inherit a given implementation of the interface from a given base class: you have to RE-implement the interface, due to the lack of multiple implementation inheritance.
I’ve described in my blog about MI an abstract example and you can apply that abstract example to a lot of classes in .NET’s API.
Well, this is interesting. In my ECOOP workshop, the Europeans stereotypes Americans by complaining that they misused inheritance for implementation not type inheritance and that implementation inheritance is, at best, a hack, if not downright immoral. The classic example of implementation inheritance is the use of an array to implement a stack. To a certain kind of person, because the Liskov substitution principle does not hold, and the public interface of the array has to be suppressed from the user, this design is evil. It is an old debate, and one that has been lost in the CLR, at least in terms of MI.
The bottom line is: the CLR has chosen a SI + interface model. C++ had chosen a different model. There is a gap between the two. And it is a design decision whether or not to attempt to bridge that gap. We have decided not to bridge this gap. We have, however, decided to bridge the gap between the CLR and ISO C++ in terms of deterministic finalization [destructors] and support for the copy constructor. These seems more fundamental to the language – real cornerstones.