Path: utzoo!utgpu!news-server.csri.toronto.edu!cs.utexas.edu!samsung!think.com!barmar From: barmar@think.com (Barry Margolin) Newsgroups: comp.object Subject: Re: Examples of Multiple Inheritance? Message-ID: <1990Dec6.020928.13319@Think.COM> Date: 6 Dec 90 02:09:28 GMT References: <60700005@inmet> Sender: news@Think.COM Organization: Thinking Machines Corporation, Cambridge MA, USA Lines: 72 One thing I haven't seen anyone mention in this discussion is that the utility and elegance of MI is strongly influenced by the quality of the MI scheme provided by the language. I'm familiar with Flavors and CLOS, and have a passing knowledge of C++, and C++ doesn't make it nearly as easy to make use of MI (perhaps Dan Weinreb, who has migrated from Flavors to C++, could comment on this). For instance, Flavors' and CLOS's method combination methodology makes a "mixin" approach to OO very elegant. Specifically, the handling of method name conflicts among a class and its superclasses strongly affects the usability of the inheritance mechanism. In Flavors and CLOS a generic function can be specified to invoke all the methods (perhaps combining the results in some way), and "before" and "after" methods can be defined so that a mixin class may easily augment the behavior of an object. In C++, inherited member name conflicts must be resolved explicitly by the programmer; if a new parent class is added to a class the programmer must then update all the conflicting member functions to implement the method combination. The result of this is that C++ functions with complex MI schemes end up with lots of functions that look like: class Parent1 { public: function(); }; class Parent2 { public: function(); }; class Parent3 { public: function(); }; class Derived : public Parent1, public Parent2, public Parent3 { public : function(); }; void Derived::function() { // encapsulate the ambiguous inherited members Parent1::function(); Parent2::function(); Parent3::function(); // Now do stuff specific to the Derived class } When looking at this one is tempted to say that it would work just as well if the parents were separate objects, and Derived simply contained them; it would look like: class Derived { public: function(); protected: Parent1 p1; Parent2 p2; Parent3 p3; }; void Derived::function() { // forward the operation to the "parents" p1.function(); p2.function(); p3.function(); // Now do stuff specific to the Derived class } Of course, the reason not to do this is that this forces Derived to reimplement *all* the member functions of all its parents, not just the ones that conflict. C++ multiple inheritance is reasonable when all the parents provide disjoint pieces of behavior (e.g. iostream inheriting from istream and ostream), but it's painful when the parents are all contributing to the same behavior (e.g. in a window system, each parent may want to modify the way the window is displayed). -- Barry Margolin, Thinking Machines Corp. barmar@think.com {uunet,harvard}!think!barmar Brought to you by Super Global Mega Corp .com