[23] Inheritance — what your mother never told you
(Part of C++ FAQ Lite, Copyright © 1991-2001, Marshall Cline, cline@parashift.com)


FAQs in section [23]:


[23.1] Is it okay for a non-virtual function of the base class to call a virtual function? NEW!

[Recently created (in 4/01). Click here to go to the next FAQ in the "chain" of recent changes.]

Yes. It's sometimes (not always!) a great idea. For example, suppose all Shape objects have a common algorithm for printing, but this algorithm depends on their area and they all have a potentially different way to compute their area. In this case Shape's area() method would necessarily have to be virtual (probably pure virtual) but Shape::print() could, if we were guaranteed no derived class wanted a different algorithm for printing, be a non-virtual defined in the base class Shape.

 #include "Shape.hpp"
 
 void Shape::print() const
 {
     float a = this->area();  
// area() is pure virtual
     
// ...
 }

TopBottomPrevious sectionNext sectionSearch the FAQ ]


[23.2] That last FAQ confuses me. Is it a different strategy from the other ways to use virtual functions? What's going on? NEW!

[Recently created (in 4/01). Click here to go to the next FAQ in the "chain" of recent changes.]

Yes, it is a different strategy. Yes, there really are two different basic ways to use virtual functions:

  1. Suppose you have the situation described in the previous FAQ: you have a method whose overall structure is the same for each derived class, but has little pieces that are different in each derived class. So the algorithm is the same, but the primitives are different. In this case you'd write the overall algorithm in the base class as a public method (that's sometimes non-virtual), and you'd write the little pieces in the derived classes. The little pieces would be declared in the base class (they're often protected, they're often pure virtual, and they're certainly virtual), and they'd ultimately be defined in each derived class. The most critical question in this situation is whether or not the public method containing the overall algorithm should be virtual. The answer is to make it virtual if you think that some derived class might need to override it.
  2. Suppose you have the exact opposite situation from the previous FAQ, where you have a method whose overall structure is different in each derived class, yet it has little pieces that are the same in most (if not all) derived classes. In this case you'd put the overall algorithm in a public virtual that's ultimately defined in the derived classes, and the little pieces of common code can be written once (to avoid code duplication) and stashed somewhere (anywhere!). A common place to stash the little pieces is in the protected part of the base class, but that's not necessary and it might not even be best. Just find a place to stash them and you'll be fine. Note that if you do stash them in the base class, you should normally make them protected, since normally they do things that public users don't need/want to do. Assuming they're protected, they probably shouldn't be virtual: if the derived class doesn't like the behavior in one of them, it doesn't have to call that method.

For emphasis, the above list is a both/and situation, not an either/or situation. In other words, you don't have to choose between these two strategies on any given class. It's perfectly normal to have method f() correspond to strategy #1 while method g() corresponds to strategy #2. In other words, it's perfectly normal to have both strategies working in the same class.

TopBottomPrevious sectionNext sectionSearch the FAQ ]


[23.3] When my base class's constructor calls a virtual function, why doesn't my derived class's override of that virtual function get invoked?

During the class Base's constructor, the object isn't yet a Derived, so if Base::Base() calls a virtual function virt(), the Base::virt() will be invoked, even if Derived::virt() exists.

Similarly, during Base's destructor, the object is no longer a Derived, so when Base::~Base() calls virt(), Base::virt() gets control, not the Derived::virt() override.

You'll quickly see the wisdom of this approach when you imagine the disaster if Derived::virt() touched a member object from class Derived. In particular, if Base::Base() called the virtual function virt(), this rule causes Base::virt() to be invoked. If it weren't for this rule, Derived::virt() would get called before the Derived part of a Derived object is constructed, and Derived::virt() could touch unconstructed member objects from the Derived part of a Derived object. That would be a disaster.

TopBottomPrevious sectionNext sectionSearch the FAQ ]


[23.4] Should a derived class replace ("override") a non-virtual function from a base class?

It's legal, but it ain't moral.

Experienced C++ programmers will sometimes redefine a non-virtual function for efficiency (e.g., if the derived class implementation can make better use of the derived class's resources) or to get around the hiding rule. However the client-visible effects must be identical, since non-virtual functions are dispatched based on the static type of the pointer/reference rather than the dynamic type of the pointed-to/referenced object.

TopBottomPrevious sectionNext sectionSearch the FAQ ]


[23.5] What's the meaning of, Warning: Derived::f(float) hides Base::f(int)?

It means you're going to die.

Here's the mess you're in: if Base declares a member function f(int), and Derived declares a member function f(float) (same name but different parameter types and/or constness), then the Base f(int) is "hidden" rather than "overloaded" or "overridden" (even if the Base f(int) is virtual).

Here's how you get out of the mess: Derived must have a using declaration of the hidden member function. For example,

 class Base {
 public:
   void f(int);
 };
 
 class Derived : public Base {
 public:
   using Base::f;    
// This un-hides Base::f(int)
   void f(double);
 };

If the using syntax isn't supported by your compiler, redefine the hidden Base member function(s), even if they are non-virtual. Normally this re-definition merely calls the hidden Base member function using the :: syntax. E.g.,

 class Derived : public Base {
 public:
   void f(double);
   void f(int i) { Base::f(i); }  
// The redefinition merely calls Base::f(int)
 };

TopBottomPrevious sectionNext sectionSearch the FAQ ]


[23.6] What does it mean that the "virtual table" is an unresolved external?

If you get a link error of the form "Error: Unresolved or undefined symbols detected: virtual table for class Fred," you probably have an undefined virtual member function in class Fred.

The compiler typically creates a magical data structure called the "virtual table" for classes that have virtual functions (this is how it handles dynamic binding). Normally you don't have to know about it at all. But if you forget to define a virtual function for class Fred, you will sometimes get this linker error.

Here's the nitty gritty: Many compilers put this magical "virtual table" in the compilation unit that defines the first non-inline virtual function in the class. Thus if the first non-inline virtual function in Fred is wilma(), the compiler will put Fred's virtual table in the same compilation unit where it sees Fred::wilma(). Unfortunately if you accidentally forget to define Fred::wilma(), rather than getting a Fred::wilma() is undefined, you may get a "Fred's virtual table is undefined". Sad but true.

TopBottomPrevious sectionNext sectionSearch the FAQ ]


[23.7] How can I set up my class so it won't be inherited from? NEW!

[Recently created (in 8/01). Click here to go to the next FAQ in the "chain" of recent changes.]

This is known as making the class "final" or "a leaf." There are two ways to do it: an easy technical approach and an even easier non-technical approach.

TopBottomPrevious sectionNext sectionSearch the FAQ ]


[23.8] How can I set up my member function so it won't be overridden in a derived class? NEW!

[Recently created (in 8/01). Click here to go to the next FAQ in the "chain" of recent changes.]

This is known as making the method "final" or "a leaf." Here's an easy-to-use solution to this that gives you 90+% of what you want: simply add a comment next to the method and rely on code reviews or random maintenance activities to find violators. The comment could say, for example, // We'll fire you if you override this method or perhaps more likely, /*final*/ void theMethod();.

The advantages to this technique are (a) it is extremely easy/fast/inexpensive to use, and (b) it is quite effective in practice. In other words, you get 90+% of the benefit with almost no cost — lots of bang per buck.

(I'm not aware of a "100% solution" to this problem so this may be the best you can get. If you know of something better, please feel free to email me. But please do not email me objecting to this solution because it's low-tech or because it doesn't "prevent" people from doing the wrong thing. Who cares whether it's low-tech or high-tech as long as it's effective?!? And nothing in C++ "prevents" people from doing the wrong thing. Using pointer casts and pointer arithmetic, people can do just about anything they want. C++ makes it easy to do the right thing, but it doesn't prevent espionage. Besides, the original question (see above) asked for something so people won't do the wrong thing, not so they can't do the wrong thing.)

In any case, this solution should give you most of the potential benefit at almost no cost.

TopBottomPrevious sectionNext sectionSearch the FAQ ]


E-Mail E-mail the author
C++ FAQ LiteTable of contentsSubject indexAbout the author©Download your own copy ]
Revised Aug 15, 2001