Path: utzoo!utgpu!watmath!clyde!att!osu-cis!tut.cis.ohio-state.edu!mailrus!ames!amdcad!sun!pitstop!sundc!seismo!uunet!mcvax!ukc!etive!lfcs!db From: db@lfcs.ed.ac.uk (Dave Berry) Newsgroups: comp.lang.c++ Subject: Re: Problem with multiple inheritance? (Long) Message-ID: <941@etive.ed.ac.uk> Date: 11 Nov 88 14:38:42 GMT References: <8335@nlm-mcs.arpa> <3438@jpl-devvax.JPL.NASA.GOV> <1140@microsoft.UUCP> <3451@jpl-devvax.JPL.NASA.GOV> Sender: news@etive.ed.ac.uk Reply-To: db@lfcs.ed.ac.uk (Dave Berry) Organization: Laboratory for the Foundations of Computer Science, Edinburgh U Lines: 130 Caveat: the following article is based on my general knowledge of programming languages, my experience with C++, and David Smyth's article. I may have missed the point on several occasions. In article <3451@jpl-devvax.JPL.NASA.GOV> david@beowulf.JPL.NASA.GOV (David Smyth) writes: > >Shopiro gave the following example: > >class List { ... }; // Something which can be on a list. >class Window { ... }; // A window. > >class Bordered : > public List, virtual public Window > { ... }; // A bordered window on a list. > >class Labeled : > public List, virtual public Window > { ... }; // A labeled window on a list. > >class Fancy : > public Bordered, public Labeled > { ... }; // A bordered, labeled window on TWO lists - > // clear as mud, right? This makes sense to me. Like any language construct, the details have to be explained (think of call-by-value vs. call-by-reference vs. call-by-name procedure parameters etc.), but this construct fits with the rest of the way C++ does things. >And here is more illumination: if the object library author did not >specify that Window was a virtual base class of Bordered and Labeled, >but just a base class, then Fancy would consist of TWO windows! You >would not have one with characteristics (member data and member >functions) of both, rather you would have a new class with a Bordered >window, and a Labeled window. Usually the sharing of objects in any hierarchy has to be specified explicitly. There isn't a rule that will always give the desired results, because the desired results vary from case to case. >Note that the derived classes change radically when a base class is >altered - Fancy will be on a single list, not two, if the definition of >EITHER Bordered or Labeled is changed to make List a virtual base >class. That is theoretically OK, since any change to a base class may >impact any derived classes. Practically, however, it is a can of worms. What I find odd is the assumption that all occurrences of a virtual class will be the same. In an analogous hierarchy in ML the user can specify which sub-components are shared (i.e. the same), to any depth in the visible hierarchy. I'd expected C++ to offer the same facility. In the above example, I would have expected the programmer to have to write "sharing Bordered.List == Labeled.List" if he wanted Fancy objects to be on only one list. This might have gone some way to reducing unexpected effects from changes in base classes. Incidentally, our ML group finds that hierarchies often have a rhomdoid shape, with the "classes" at the top of the rhombus specifying lots of sharing between those in the middle. Someone using the rhombus as a whole rarely needs to know its internal hierarchy. If the same effect applies to C++, the effects may not be as bad as David expects. (To prevent misunderstanding, let me stress that the hierarchy of structures in ML is analogous to an inheritance hierachy, but is substantially different. Also, I'm comparing the languages, just one feature from each.) >Other problems: when Fancy overrides a Window method, its OK, because >Fancy has only one Window. If it overrides a List method, nope: >ambiguous. This is obvious... >True, the compiler says so. But the author of Fancy >must know the inheritance of the classes in the library to make >sure the appropriate method is overriden. Notice that simply a >complete list of names visible in the Bordered and Labeled classes >is not enough: the client (Fancy) author must also know the internal >structure of the library. Not very re-usable. So you need more documentation to use a library. How does this stop it from being re-usable? >And more: you lose substitutablity when you use a virtual base class. >Fancy cannot be on a list of windows, because you can't cast a pointer >declared as a pointer to Window into a pointer to Fancy, or Bordered >or Labeled for that matter. Now this is a problem. Can't C++ introduce a notion of cast that is more sophisticated than the C notion, to handle cases like this? >What to use instead: Member objects! > >class List { ... }; // exactly as above. >class Window { ... }; // ditto. >class Border { ... }; // Border attrs and methods. >class Label { ... }; // Label attrs and methods. > >class Bordered : Window // Inherit most significant characteristics > { List borderedList; > Border myBorder; > ... }; // Window with border, on a list. > >class Labeled : Window > { List labeledList; > Label myLabel; > ... }; // Window with label, on a list. > >class Fancy : Window > { List labeledList, borderedList; > Border myBorder; > Label myLabel; > ... }; // Labeled, bordered window on two lists. This is certainly a simpler structure. If multiple inheritance doesn't provide the possibility of lists of a given type including objects of a sub-type of that type, with virtual members working properly, I don't think multiple inheritance adds much, if anything, to the language. I guess it make function calls shorter (you don't have to qualify the member name with the class name(s)), but this hardly justifies the increased complexity. If proper lists were provided, then Jon's multiple inheritance structure would provide more facilities than David's member objects structure. The choice of which to use would depend on what facilities were needed. Having gone on at such length, I hope I've got this right... Dave Berry, Laboratory for Foundations of Computer Science, Edinburgh. db%lfcs.ed.ac.uk@nss.cs.ucl.ac.uk !mcvax!ukc!lfcs!db