When to use virtual assignment

Assignment, or operator=, is a function that requires careful consideration before you make it virtual. You might think that, like other members, it is better to make it virtual to be safe. Otherwise, you run into problems like this:

      void Bar(TFoo &arg)
      {
         arg = value;
      }
If TFoo::operator= is not virtual, this assignment will slice the object. So shouldn't assignment always be virtual?

There is a catch. Because C++ automatically overloads assignment in each new class, you need to supply two assignment operators. If you have a base TBase and its derived class TDerived, and TBase has virtual operator=, then TDerived might need to override TBase::operator=, in addition to defining its own operator=. If TDerived defines virtual operator=, its subclasses must override TBase::operator= and TDerived::operator=, in addition to defining their own operator=. And so on.

Clearly this gets out of hand quickly. It only makes sense to make assignment virtual when there are additional restrictions that keep this geometric progression from occurring.

Consider a shallow class hierarchy, where the classes are convertible between each other. See the discussion of TColor in "Equality" on page 115 for an example. Another example is TText; different subclasses should be convertible among themselves, and the class hierarchy is unlikely to get deep.

Another possibility is that the inherited virtual operator= would rarely be overridden. Overriding happens only if the base class implementation is adequate in most cases (unlikely), or if it is implemented in terms of other virtual functions that subclasses do override. This latter method is better than overriding operator= itself because the other virtual functions do not automatically get overloaded by the compiler in every subclass.

There is also an issue with implementing derived class assignment. Usually, when you do this, you invoke the assignment operator of your base class (or classes). However, if your base class does have a virtual operator=, or if the base's operator= is otherwise defined to work correctly for all derived classes (for example, it calls virtual functions to do all the work), you must be careful when writing operator= for derived classes. If you call the base class version too, the assignment will likely occur twice! In this situation, you need to think of the derived class assignment as a special case where more specific argument types are known. If special case handling is not necessary, the derived version can just call the base class version:

      TDerived& TDerived::operator=(const TDerived &d)
      {
          TBase::operator=(d);
      }
Always supply an assignment operator in such cases, because the default version C++ supplies will surely do the wrong thing: in addition to calling the base class operator, it will copy all your data members.

On the other hand, if a class is going to be used in situations where references are likely to be assigned to, either the base class operator= must always work or assignment has to be virtual. Otherwise, clients get sliced objects, which leads to subtle bugs.

This is an area of C++ where there is no single correct approach. Theoretically, the right thing is to always make assignment virtual, but doing so leads to problems of its own. Because of these trade-offs, whether to make assignment virtual is something you should consider carefully, in consultation with an architect.


[Contents] [Previous] [Next]
Click the icon to mail questions or corrections about this material to Taligent personnel.
Copyright©1995 Taligent,Inc. All rights reserved.

Generated with WebMaker