C++ Programming: When Is a Virtual Function Not?

By Stephen R. Davis

In C++, just because you think that a particular function call is bound late doesn’t mean that it is. If not declared with the same arguments in the subclasses, the member functions are not overridden polymorphically, whether or not they are declared virtual.

One exception to the identical declaration rule is that if the member function in the base class returns a pointer or reference to a base class object, an overridden member function in a subclass may return a pointer or reference to an object of the subclass. In other words, the function makeACopy() is polymorphic, even though the return type of the two functions differ:

class Base
{
  public:
    // return a copy of the current object
    Base* makeACopy();
};
class SubClass : public Base
{
  public:
    // return a copy of the current object
    SubClass* makeACopy();
};
void fn(Base& bc)
{
    Base* pCopy = bc.makeACopy();
    // proceed on...
}

In practice, this is quite natural. A makeACopy() function should return an object of type SubClass, even though it might override BaseClass::makeACopy().

This business of silently deciding when a function is overridden and when not is a source of error in C++; so much so that the 2011 standard introduced the descriptor override that the programmer can use to indicate her intent to override a base class function.

C++ generates a compiler error if a function is declared override but does not, in fact, override a base class function for some reason (such as a mismatched argument) as in the following example:

class Student
{
  public:
    virtual void addCourseGrade(double grade);
};
class GradStudent : public Student
{
  public:
    virtual void addCourseGrade(float grade) override;
};

This snippet generates a compile-time error because the method GradStudent::addCourseGrade(float) was declared override but it does not, in fact, override the base class function Student::addCourseGrade(double) because the argument types don’t match.

The programmer can also declare a function as not overrideable using the final keyword, even if that function itself overrides some earlier base class function, as demonstrated in the following additional PostDoc class:

class GradStudent : public Student
{
  public:
    virtual void addCourseGrade(double grade) final;
};
class PostDoc : public GradStudent
{
  public:
      virtual void addCourseGrade(double grade);
};

Since Student::addCourseGrade() is marked final, the declaration of PostDoc::addCourseGrade() generates an error because it attempts to override the Student method.

In addition, an entire class can be declared final:

class GradStudent final: public Student

This affects more than just the virtual methods of the class. A final class cannot be inherited from at all.