TITLE: Value and reference semantics in C++ again RESPONSES: 3 May 93 RM = rmartin@uunet.uu.net!rcm (Robert Martin) JS = maxtal@physics.su.OZ.AU (John Max Skaller) [RM] I am using the term ADT from Stroustrup and Coplien to mean "Abstract Data Type" which is an interface with no implementation. i.e. a class with nothing but pure virtual functions. A CDT on the other hand is a concrete data type. i.e. A data type which can be instantiated, copied, etc... Somewhere in the middle between ADT and CDT is the concept of an abstract class. Abstract classes can have some implementation, but they usually still have some interfaces without implemenations and therefore cannot be instantiated.. [JS] My conjecture is that this is inadequate. I am conjecturing that value oriented classes and object oriented classes are entirely different and distinct and require different programming techniques, and that it is hard, if not impossible, to mix the two. In that case, my classification is something like: ADT (value oriented class): no public inheritance allowed: may not be derived from another class, nor may it be used as a base class. In return, it can be freely copied. The address of an object or such a class is insignificant, and perhaps one should prevent the address being taken. OOT: (object oriented class): there are two types: a) abstract class. Has pure virtual functions. b) implementation subclass. Must derive from the abstract class using virtual inheritance. An OOT should be refered to by a pointer to the abstract class. It cannot be copied. The implementation should probably be hidden after construction. [RM] Anyway, polymorphism in C++ is not difficult to achieve. But you must remember that polymorphic access must be through a pointer or a reference to a base class. [JS] No, that is my whole point: you should *not* have to remember this at all. If you do anything that doesnt work properly the compiler should stop you. [RM] But, in C++ you *DO* have to remember this. It's part of the language. [JS] I contend you should NOT have to remember this to use a class. My conjecture is that it *is* possible to construct object oriented classes in such a way that it is not necessary to remember it to *use* the class and that classes contructed so that you do have to remember constitue a design error. [RM] It all works quite nicely, and is no bother at all. [JS] No, it requires a well constructed example such as the one you gave to demonstrate how to do it properly. [RM] If you mean to say that an example is necessary to understand how to do it right. OK. This in not uncommon in an any discipline. [JS] Yes, thats what I meant. I suggest that it is not at all obvious how to use C++ properly, and that no one (self included :-) really understands the issues properly. In particular, I suggest the 'obvious' technique of public derivation of Orthodox Idiom (ADT) classes is almost always a design error (unless special constraints are met, one of which is not introducing any extra state that can be sliced off). [RM] If you mean to say that the example is contrived and not applicable to real world problems, I must differ. I solve lots of real world problems with architectures like this all the time. [JS] No, I meant that I like to see examples you write: they are always well thought out and usually provide excellent examples of how to design things well. That doesnt mean that there may not be issues or design problems raised by such examples, but it means most of the issues raised by your examples are fairly fundamental in nature. The 'caveats', if any are clear: even if a technique is not completely safe, your examples tend to document where programmers must take responsibility, and that is very important. [JS] However users *beware*. This is no object Heirarchy. You had better NOT derive from Circle or Square publically, because those classes will fail. Because the copy constructors and assignment operators of those classes are public! [RM] Why will they fail? Let's say I create NamedCircle which adds a String member to hold the the Circle's name. Now it is true that if you do Circle c; NamedCircle nc; c=nc; // slice. Then the NamedCircle will lose it's name. But this is correct behavior in C++. Both c and nc are values, and the value for a Circle cannot hold a NamedCircle. On the other hand pointers and references to Circle *can* point to NamedCircle without an slicing taking place. [JS] Yes, but there are *constraints* that must be followed. I said above that you must not add any extra state. Now I must change that to say 'essential extra state', since losing the name of a circle may not be regarded as essential. [RM] So, if you wan't polymorphism, use pointers or references. If you want something that looks like value syntax, but is really disguised reference syntax, use Smalltalk. [JS] Yes, but I want to go further: I want to design classes that act as objects or values *in themselves* and do not rely on the programmer using them responsibly. I at least want to find the answer to the question: "What can you do to an ADT class when deriving a new one that is safe, and what is not?" For example: suppose class Base is an ADT class with virtual functions. The[n] it seems reasonable to require that Derived d; Base &br=d; Base bs=d; assert( bs.f() == br.f() ) that is, slicing must NOT change the return value of any function. Now consider: class Base { public: virtual int g()const {return 0;} }; Well, you cant override 'g' by my rules. In general, for polymorphic objects, slicing must *ADD* extra potential states. That is, you can only slice off constraints. You can slice away *non-essential* extra state, that is, state that cannot be accessed polymorphically. Well, this again re-inforces my concept that value oriented semantics and object oriented semantics work in exactly the opposite directions. In one case inheritance is 'covariant' and the other it is 'contravariant'?