Virtual Considerations in C++ - dummies

Virtual Considerations in C++

By Stephen R. Davis

You need to keep in mind a few things when using virtual functions in C++. First, static member functions cannot be declared virtual. Because static member functions are not called with an object, there is no runtime object upon which to base a binding decision.

Second, specifying the class name in the call forces a call to bind early, whether or not the function is virtual. For example, the following call is to Base::fn() because that’s what the programmer indicated, even if fn() is declared virtual:

void test(Base& b)
  b.Base::fn();     // this call is not bound late

Finally, constructors cannot be virtual because there is no (completed) object to use to determine the type. At the time the constructor is called, the memory that the object occupies is just an amorphous mass. It’s only after the constructor has finished that the object is a member of the class in good standing.

By comparison, the destructor should almost always be declared virtual. If not, you run the risk of improperly destructing the object, as in the following circumstance:

class Base
class SubClass : public Base
void finishWithObject(Base* pHeapObject)
    // with object...
    // now return it to the heap
    delete pHeapObject; // this calls ~Base() no matter
}                       // the runtime type of 
                        // pHeapObject

If the pointer passed to finishWithObject() really points to a SubClass, the SubClass destructor is not invoked properly — because the destructor has not been declared virtual, it’s always bound early. Declaring the destructor virtual solves the problem.

So when would you not want to declare the destructor virtual? There’s only one case. Virtual functions introduce a “little” overhead.

More specifically: When the programmer defines the first virtual function in a class, C++ adds an additional, hidden pointer — not one pointer per virtual function, just one pointer if the class has any virtual functions. A class that has no virtual functions (and does not inherit any virtual functions from base classes) does not have this pointer.

Now, one pointer doesn’t sound like much, and it isn’t unless the following two conditions are true:

  • The class doesn’t have many data members (so that one pointer represents a lot compared to what’s there already).

  • You intend to create a lot of objects of this class (otherwise, the overhead doesn’t make any difference).

If these two conditions are met and your class doesn’t already have virtual member functions, you may not want to declare the destructor virtual.

Except for this one case, always declare destructors to be virtual, even if a class is not subclassed (yet) — you never know when someone will come along and use your class as the base class for her own. If you don’t declare the destructor virtual, then declare the class final (if your compiler supports this feature) and document it!