DiscussionWhen you build class hierarchies, usually the intent is to get polymorphic behavior. You want objects, once created, to preserve their type and identity. This goal conflicts with C++'s usual object copy semantics because the copy constructor is not virtual and cannot be made virtual. Consider: class B {/* … */}; class D : public B {/* … */}; void Transmogrify( B obj ); // oops: takes an object by value void Transubstantiate( B& obj ){ // ok: takes a reference Transmogrify( obj ); // oops: slices the object // … } D d; Transubstantiate( d ); The programmer's intent to manipulate B and B-derived objects polymorphically. However, by mistake (fatigue? too little caffeine?) the programmer either forgot to write an & in transmogrify's signature, or intended to create a copy but did it the wrong way. The code will compile fine, but when TRansmogrify is called the passed-in D object will, well, transmogrify into a B. This is because a by-value pass involves a call to B::B( const B& ), that is, B's copy constructor, and the const B& passed in will be the automatically converted reference to d. Wiping away all the dynamic, polymorphic behavior that prompted you to use inheritance in the first place is most likely what you don't want. If as the author of B you do want to allow slicing, but you don't want callers to slice easily or by accident, here's one option we mention for completeness but do not recommend in code that needs to be portable: You can make B's copy constructor explicit. This can help avoid implicit slicing, but it also prevents all pass-by-value (this can be good for base classes, which shouldn't be instantiable anyway; see Item 35): // Making the copy constructor explicit (has side effects, needs improvement) class B {// … public: explicit B( const B& rhs ); }; class D : public B {/* … */}; Calling code can still slice if it really wants to, but has to be, well, explicit about it: void Transmogrify( B obj ); // note: now can't ever be called (!) void Transmogrify2( const B& obj ) { // an idiom to explicitly say "I want to take B b( obj ); // obj by value anyway (and possibly slice it)" // … } B b; // base classes shouldn't be concrete (see D d; // Item 35), but let's imagine that B is Transmogrify( b ); // now an error (or should be; see note) Transmogrify( d ); // now an error (or should be; see note) Transmogrify2( d ); // ok Note: As of this writing, some compilers incorrectly accept one or both of these calls to transmogrify. This idiom is standard, but it's not (yet) completely portable. There is a better way that portably achieves the goal of preventing slicing and delivers more value to boot. Let's say that functions like transmogrify really do want to take a full deep copy without knowing the actual derived type of the object it's being given. The more general idiomatic solution is to make the copy constructor for base classes protected (so that a function like TRansmogrify won't call it accidentally) and rely on a virtual Clone function instead: // Adding Clone (first cut, better but still needs improvement) class B {// … public: virtual B* Clone() const = 0; protected: B( const B& ); }; class D : public B { // … public: virtual D* Clone() const {return new D(*this); } protected: D( const D& rhs ) : B( rhs ) {/* … */} }; Now, trying to slice will (portably) generate a compile-time error, and making Clone a pure virtual function forces immediately-derived classes to override it. Unfortunately, this solution still has two problems that the compiler cannot detect: A further-derived class in the hierarchy can still forget to implement Clone, and an override of Clone could implement it improperly so that the copy is not the same type as the original. Clone should apply the Nonvirtual Interface pattern (NVI; see Item 39), which separates Clone's public and virtual natures and lets you plant a valuable assertion: class B {// … public: B* Clone() const { // nonvirtual B* p = DoClone(); assert( typeid(*p) == typeid(*this) && "DoClone incorrectly overridden" ); return p; // check DoClone's returned type } protected: B( const B& ); virtual B* DoClone() const = 0; }; Clone is now the nonvirtual interface used by all calling code. Derived classes only need to override DoClone. The assert will flag any copy that doesn't have the same type as the original, thus signaling that some derived class forgot to override the DoClone function; asserts are, after all, to report such programming errors (see Items 68 and 70). |