From an OO perspective, it is the single most important feature of C++: [6.8], [6.9].
A virtual function allows derived classes to replace the implementation provided by the base class. The compiler makes sure the replacement is always called whenever the object in question is actually of the derived class, even if the object is accessed by a base pointer rather than a derived pointer. This allows algorithms in the base class to be replaced in the derived class, even if users don't know about the derived class.
The derived class can either fully replace ("override") the base class member function, or the derived class can partially replace ("augment") the base class member function. The latter is accomplished by having the derived class member function call the base class member function, if desired.
[ Top | Bottom | Previous section | Next section | Search the FAQ ]
When you have a pointer to an object, the object may actually be of a class
that is derived from the class of the pointer (e.g., a
Static typing means that the legality of a member function invocation is checked at the earliest possible moment: by the compiler at compile time. The compiler uses the static type of the pointer to determine whether the member function invocation is legal. If the type of the pointer can handle the member function, certainly the pointed-to object can handle it as well. E.g., if Vehicle has a certain member function, certainly Car also has that member function since Car is a kind-of Vehicle.
Dynamic binding means that the address of the code in a member function invocation is determined at the last possible moment: based on the dynamic type of the object at run time. It is called "dynamic binding" because the binding to the code that actually gets called is accomplished dynamically (at run time). Dynamic binding is a result of virtual functions.
[ Top | Bottom | Previous section | Next section | Search the FAQ ]
Non-virtual member functions are resolved statically. That is, the member function is selected statically (at compile-time) based on the type of the pointer (or reference) to the object.
In contrast, virtual member functions are resolved dynamically (at run-time). That is, the member function is selected dynamically (at run-time) based on the type of the object, not the type of the pointer/reference to that object. This is called "dynamic binding." Most compilers use some variant of the following technique: if the object has one or more virtual functions, the compiler puts a hidden pointer in the object called a "virtual-pointer" or "v-pointer." This v-pointer points to a global table called the "virtual-table" or "v-table."
The compiler creates a v-table for each class that has at least one virtual
function. For example, if class Circle has virtual functions for
During a dispatch of a virtual function, the run-time system follows the object's v-pointer to the class's v-table, then follows the appropriate slot in the v-table to the method code.
The space-cost overhead of the above technique is nominal: an extra pointer per object (but only for objects that will need to do dynamic binding), plus an extra pointer per method (but only for virtual methods). The time-cost overhead is also fairly nominal: compared to a normal function call, a virtual function call requires two extra fetches (one to get the value of the v-pointer, a second to get the address of the method). None of this runtime activity happens with non-virtual functions, since the compiler resolves non-virtual functions exclusively at compile-time based on the type of the pointer.
Note: the above discussion is simplified considerably, since it doesn't account for extra structural things like multiple inheritance, virtual inheritance, RTTI, etc., nor does it account for space/speed issues such as page faults, calling a function via a pointer-to-function, etc. If you want to know about those other things, please ask comp.lang.c++; PLEASE DO NOT SEND E-MAIL TO ME!
[ Top | Bottom | Previous section | Next section | Search the FAQ ]
It's surprisingly easy.
Suppose there is a base class Vehicle with derived classes Car and "Truck". The code traverses a list of Vehicle objects and does different things depending on the type of Vehicle. For example it might weigh the "Truck" objects (to make sure they're not carrying too heavy of a load) but it might do something different with a Car object check the registration, for example.
The initial solution for this, at least with most people, is to use an if statement. E.g., "if the object is a "Truck", do this, else if it is a Car, do that, else do a third thing":
The problem with this is what I call "else-if-heimer's disease" (say it fast
and you'll understand). The above code gives you else-if-heimer's disease
because eventually you'll forget to add an
The solution is to use dynamic binding rather than dynamic typing. Instead of having (what I call) the live-code dead-data metaphor (where the code is alive and the car/truck objects are relatively dead), we move the code into the data. This is a slight variation of Bertrand Meyer's Inversion Principle.
The idea is simple: use the description of the code within the
Then you remove the whole
Finally you move the code that used to be in the
If you actually have an else block in the original
That's it!
The point, of course, is that we try to avoid decision logic with decisions
based on the kind-of derived class you're dealing with. In other words,
you're trying to avoid
[ Top | Bottom | Previous section | Next section | Search the FAQ ]
When someone will delete a derived-class object via a base-class pointer.
In particular, here's when you need to make your destructor virtual:
Confused? Here's a simplified rule of thumb that usually protects you and usually doesn't cost you anything: make your destructor virtual if your class has any virtual functions. Rationale:
Note: if your base class has a virtual destructor, then your destructor is automatically virtual. You might need an explicit destructor for other reasons, but there's no need to redeclare a destructor simply to make sure it is virtual. No matter whether you declare it with the virtual keyword, declare it without the virtual keyword, or don't declare it at all, it's still virtual.
BTW, if you're interested, here are the mechanical details of why you
need a virtual destructor when someone says delete using a Base pointer
that's pointing at a Derived object. When you say
[ Top | Bottom | Previous section | Next section | Search the FAQ ]
An idiom that allows you to do something that C++ doesn't directly support.
You can get the effect of a virtual constructor by a virtual
In the
Users use these as if they were "virtual constructors":
This function will work correctly regardless of whether the Shape is a Circle, Square, or some other kind-of Shape that doesn't even exist yet.
Note: The return type of Circle's
Microsoft Visual C++ 6.0 does not support Covariant Return Types. MS VC++ 7.0 supports them properly, but 6.0 does not. This means:
[ Top | Bottom | Previous section | Next section | Search the FAQ ]
E-mail the author
[ C++ FAQ Lite
| Table of contents
| Subject index
| About the author
| ©
| Download your own copy ]
Revised May 2, 2003