Back to Techniques
Continue to Item 26: Limiting the number of objects of a class

Item 25: Virtualizing constructors and non-member functions.

On the face of it, it doesn't make much sense to talk about "virtual constructors." You call a virtual function to achieve type-specific behavior when you have a pointer or reference to an object but you don't know what the real type of the object is. You call a constructor only when you don't yet have an object but you know exactly what type you'd like to have. How, then, can one talk of virtual constructors?

It's easy. Though virtual constructors may seem nonsensical, they are remarkably useful. (If you think nonsensical ideas are never useful, how do you explain the success of modern physics?) For example, suppose you write applications for working with newsletters, where a newsletter consists of components that are either textual or graphical. You might organize things this way:

    class NLComponent {               // abstract base class for
    public:                           // newsletter components

      ...                             // contains at least one
    };                                // pure virtual function

    class TextBlock: public NLComponent {
    public:
      ...                             // contains no pure virtual
    };                                // functions

    class Graphic: public NLComponent {
    public:
      ...                             // contains no pure virtual
    };                                // functions

    class NewsLetter {                // a newsletter object
    public:                           // consists of a list of
      ...                             // NLComponent objects

    private:
      list<NLComponent*> components;
    };

The classes relate in this way:

The `list` class used inside `NewsLetter` is part of the Standard Template Library, which is part of the standard C++ library (see Item E49 and Item 35). Objects of type `list` behave like doubly linked lists, though they need not be implemented in that way.

`NewsLetter` objects, when not being worked on, would likely be stored on disk. To support the creation of a `Newsletter` from its on-disk representation, it would be convenient to give `NewsLetter` a constructor that takes an `istream`. The constructor would read information from the stream as it created the necessary in-core data structures:

    class NewsLetter {
    public:
      NewsLetter(istream& str);
      ...
    };

Pseudocode for this constructor might look like this,

    NewsLetter::NewsLetter(istream& str)
    {
      while (str) {
        read the next component object from str;

        add the object to the list of this
        newsletter's components;
      }
    }

or, after moving the tricky stuff into a separate function called `readComponent`, like this:

    class NewsLetter {
    public:
      ...

    private:
      // read the data for the next NLComponent from str,
      // create the component and return a pointer to it
      static NLComponent * readComponent(istream& str);
      ...
    };

    NewsLetter::NewsLetter(istream& str)
    {
      while (str) {
        // add the pointer returned by readComponent to the
        // end of the components list; "push_back" is a list
        // member function that inserts at the end of the list
        components.push_back(readComponent(str));
      }
    }

Consider what `readComponent` does. It creates a new object, either a `TextBlock` or a `Graphic`, depending on the data it reads. Because it creates new objects, it acts much like a constructor, but because it can create different types of objects, we call it a virtual constructor. A virtual constructor is a function that creates different types of objects depending on the input it is given. Virtual constructors are useful in many contexts, only one of which is reading object information from disk (or off a network connection or from a tape, etc.).

A particular kind of virtual constructor — the virtual copy constructor — is also widely useful. A virtual copy constructor returns a pointer to a new copy of the object invoking the function. Because of this behavior, virtual copy constructors are typically given names like `copySelf`, `cloneSelf`, or, as shown below, just plain `clone`. Few functions are implemented in a more straightforward manner:

    class NLComponent {
    public:
      // declaration of virtual copy constructor
      virtual NLComponent * clone() const = 0;
      ...

    };

    class TextBlock: public NLComponent {
    public:
      virtual TextBlock * clone() const         // virtual copy
      { return new TextBlock(*this); }          // constructor
      ...

    };

    class Graphic: public NLComponent {
    public:
      virtual Graphic * clone() const            // virtual copy
      { return new Graphic(*this); }             // constructor
      ...

    };

As you can see, a class's virtual copy constructor just calls its real copy constructor. The meaning of "copy" is hence the same for both functions. If the real copy constructor performs a shallow copy, so does the virtual copy constructor. If the real copy constructor performs a deep copy, so does the virtual copy constructor. If the real copy constructor does something fancy like reference counting or copy-on-write (see Item 29), so does the virtual copy constructor. Consistency — what a wonderful thing.

Notice that the above implementation takes advantage of a relaxation in the rules for virtual function return types that was adopted relatively recently. No longer must a derived class's redefinition of a base class's virtual function declare the same return type. Instead, if the function's return type is a pointer (or a reference) to a base class, the derived class's function may return a pointer (or reference) to a class derived from that base class. This opens no holes in C++'s type system, and it makes it possible to accurately declare functions such as virtual copy constructors. That's why `TextBlock`'s `clone` can return a `TextBlock*` and `Graphic`'s `clone` can return a `Graphic*`, even though the return type of `NLComponent`'s `clone` is `NLComponent*`.

The existence of a virtual copy constructor in `NLComponent` makes it easy to implement a (normal) copy constructor for `NewsLetter`:

    class NewsLetter {
    public:
      NewsLetter(const NewsLetter& rhs);
      ...

    private:
      list<NLComponent*> components;
    };

    NewsLetter::NewsLetter(const NewsLetter& rhs)
    {
      // iterate over rhs's list, using each element's
      // virtual copy constructor to copy the element into
      // the components list for this object. For details on
      // how the following code works, see Item 35.
      for (list<NLComponent*>::const_iterator it =
              rhs.components.begin();
           it != rhs.components.end();
           ++it) {

        // "it" points to the current element of rhs.components,
        // so call that element's clone function to get a copy
        // of the element, and add that copy to the end of
        // this object's list of components
        components.push_back((*it)->clone());
      }
    }

Unless you are familiar with the Standard Template Library, this code looks bizarre, I know, but the idea is simple: just iterate over the list of components for the `NewsLetter` object being copied, and for each component in the list, call its virtual copy constructor. We need a virtual copy constructor here, because the list contains pointers to `NLComponent` objects, but we know each pointer really points to a `TextBlock` or a `Graphic`. We want to copy whatever the pointer really points to, and the virtual copy constructor does that for us.

Making Non-Member Functions Act Virtual

Just as constructors can't really be virtual, neither can non-member functions (see Item E19). However, just as it makes sense to conceive of functions that construct new objects of different types, it makes sense to conceive of non-member functions whose behavior depends on the dynamic types of their parameters. For example, suppose you'd like to implement output operators for the `TextBlock` and `Graphic` classes. The obvious approach to this problem is to make the output operator virtual. However, the output operator is `operator<<`, and that function takes an `ostream&` as its left-hand argument; that effectively rules out the possibility of making it a member function of the `TextBlock` or `Graphic` classes.

(It can be done, but then look what happens:

    class NLComponent {
    public:
      // unconventional declaration of output operator
      virtual ostream& operator<<(ostream& str) const = 0;
      ...
    };

    class TextBlock: public NLComponent {
    public:
      // virtual output operator (also unconventional)
      virtual ostream& operator<<(ostream& str) const;
    };

    class Graphic: public NLComponent {
    public:
      // virtual output operator (still unconventional)
      virtual ostream& operator<<(ostream& str) const;
    };

    TextBlock t;
    Graphic g;

    ...

    t << cout;                                  // print t on cout via
                                                // virtual operator<<; note
                                                // unconventional syntax

    g << cout;                                  // print g on cout via
                                                // virtual operator<<; note
                                                // unconventional syntax

Clients must place the stream object on the right-hand side of the "`<<`" symbol, and that's contrary to the convention for output operators. To get back to the normal syntax, we must move `operator<<` out of the `TextBlock` and `Graphic` classes, but if we do that, we can no longer declare it virtual.)

An alternate approach is to declare a virtual function for printing (e.g., `print`) and define it for the `TextBlock` and `Graphic` classes. But if we do that, the syntax for printing `TextBlock` and `Graphic` objects is inconsistent with that for the other types in the language, all of which rely on `operator<<` as their output operator.

Neither of these solutions is very satisfying. What we want is a non-member function called `operator<<` that exhibits the behavior of a virtual function like `print`. This description of what we want is in fact very close to a description of how to get it. We define both `operator<<` and `print` and have the former call the latter!

    class NLComponent {
    public:
      virtual ostream& print(ostream& s) const = 0;
      ...

    };

    class TextBlock: public NLComponent {
    public:
      virtual ostream& print(ostream& s) const;
      ...

    };

    class Graphic: public NLComponent {
    public:
      virtual ostream& print(ostream& s) const;
      ...

    };

    inline
    ostream& operator<<(ostream& s, const NLComponent& c)
    {
      return c.print(s);
    }

Virtual-acting non-member functions, then, are easy. You write virtual functions to do the work, then write a non-virtual function that does nothing but call the virtual function. To avoid incurring the cost of a function call for this syntactic sleight-of-hand, of course, you inline the non-virtual function (see Item E33).

Now that you know how to make non-member functions act virtually on one of their arguments, you may wonder if it's possible to make them act virtually on more than one of their arguments. It is, but it's not easy. How hard is it? Turn to Item 31; it's devoted to that question.

Back to Techniques
Continue to Item 26: Limiting the number of objects of a class