TITLE: Abstract, concrete, and value semantics PROBLEM: shansen@adobe.com (Steve Hansen), Adobe, 3 May 93 [A] In this month's Byte magazine, Allen Holub makes some good points warning against creating a root abstract class (e.g. Collection) upon which everything else is derived, on the basis that it makes it more difficult to create hybrid classes via multiple inheritance. For those who haven't read the article, Holub points out that if you try to merge two classes via MH that have a common root class, then the compiler will complain about the ambiguity. [B] He thus recommends dispensing with the formality of using the abstract class, i.e. go ahead and define a common interface for your classes, but don't create the abstract class a la Smalltalk. I would like to hear people's opinions on this. [C] Are there really that many cases where you want to code at the generic level, using the abstract class blindly as your data type? [D] I have a bad feeling that this debate has been hashed out already, but I thought I'd try anyway. RESPONSE: maxtal@physics.su.OZ.AU (John Max Skaller), 3 May 93 [A] From your description, this is wrong. The compiler does not get mixed up because you used *virtual* inheritance, didnt you :-) [B] IMHO the wrong conclusion is drawn. The correct conclusion is not to create Object Heirarchies. At all. They dont work. Do not use public nonvirtual derivation to specialise a concrete class unless you make the copy constructor of the root private. [C] Yes. In C++ if you dont use abstract classes you are probably not doing object oriented programming. OOP is not everything, but it is sometimes a bit useful :-) [D] In C++, reusing the interface of a class and reusing the code of a class are very often in conflict. The way to resolve this seems to be to separate the interface from the code. That is, to make separate abstract and concrete classes: struct AbstractThing { virtual int func()=0; }; struct ConcreteThing : public virtual AbstractThing { int func(); }; Now, if you want to specialise, you do this: struct SpecialAbstractThing : public virtual AbstractThing { virtual int special()=0; }; and implement with say: struct SpecialConreteThing : public virtual SpecialAbstractThing { int func(); int special(); }; Notice that the SpecialConcreteThing is NOT derived from the ConreteThing. There is interface reuse, but no code reuse. You want to reuse some code? OK: struct SpecialConceteThing : public virtual SpecialAbstractThing, private ConcreteThing { int special(); }; Its important that the derivation for code reuse be private. Reusing interface and reusing code are separate and hard to mix. (You can do it as above, which is a "mixin" of sorts). The way you should NOT design this code is like this: struct ConreteThing { int x; virtual int func(){return x; } }; struct SpecialConcreteThing : public ConcreteThing { int y; virtual int special() {return y;} }; This confuses reuse of the interface and reuse of the code: it does both together and pretends that a SpecialConcreteThing 'isA' Concrete thing .. which is not true. Given: ConcreteThing * it = new SpecialConcreteThing; how do you call 'special'? You cant without downcasting and RTTI. On the other hand, you cant usually safely use an actual known SpecialConcreteThing as a ConcreteThing either: SpecialConreteThing spit; func(ConcreteThing x); func(spit); // woops, sliced off the 'special' part refunc(ConcreteThing& x); refunc(spit); // woops, cant access the 'special' part