Click here to Skip to main content
15,868,141 members
Articles / Programming Languages / C++
Article

How to make sure that you override an existing function

Rate me:
Please Sign up or sign in to vote.
4.05/5 (25 votes)
17 Nov 20034 min read 135.6K   19   54
An article on how you can make sure that you override an existing function in a base class, so that if the base class function is changed without your knowledge, you get an error.

Introduction

Experienced C++ programmers often have the habit of expecting that when something compiles, it is typesafe and it is therefore correct. Even though it may not always be true, this habit grows because it is very often true. A couple of days ago I ran into one of those situations where a compiling program, which I expected to work (because it compiled and it passed my testing) didn't do what I would have expected in a particular situation. I debugged it to find out what the problem was. It turned out that I had added a parameter to a member function of a certain class, but that I had forgotten to add that same parameter to a member function in a derived class that was supposed to be overriding the member function of the base class. Normally I would have noticed this, because the member function in the derived class actually called the member function of the base class that it was supposed to override. However, I didn't notice it this time because I had added a default value to the new parameter. This way, all code that used the member function would still work, I thought. But exactly this expectation caused the override in the derived class to silently cease being an override, which was very annoying indeed!

I'll give you an example of the situation I ran into. Say you've got the following code:

class A
{
public:  
    virtual void foo()   
    {     
        std::cout << "A::foo" << endl;   
    }
};

class B : public A
{
public:  
    virtual void foo()   
    {    std::cout << "B::foo" << endl;  
    }
};

int main()
{  
    A* a = new B;  a->foo(); // prints "B::foo".
}

You get into a situation where foo needs an extra parameter, and you change it like this:

class A
{
public:  
    virtual void foo(int n = 3)  
    {    
        std::cout << "A::foo(" << n << ")" << endl;  
    }
};
class B : public A
{
public:  
    virtual void foo()  
    {    std::cout << "B::foo" << endl;  
    }
};

int main()
{  
    A* a = new B;  a->foo(); // prints "B::foo".
}

Because you're smart, you've added a default value to the new parameter, so that all the code using A wouldn't have to be changed. Surprisingly, the program now prints "A::foo(3)" instead of "B::foo" as it did before. How could this happen? Well, obviously you forgot to change B as well, and unfortunately the compiler didn't give you a warning about this. And how could it? It doesn't know that B's foo is supposed to override A's foo!

After thinking about what happened for a while, I decided to do something about it. I wrote a macro that I could use in derived classes, which would make absolutely sure that overriding member functions were actually overriding a base class member with the exact same signature. I'm making it available for the community so that they don't have to fall into the same trap that I fell into.

Using the code

Using this macro is pretty easy. Let's say you have a class A, and a class B derived from it. Say class A has a member function A::foo that you want to override in class B. Normally, you would write:

class B
{  
    // ...  void foo(int,double);  
    // ...
};

However, when you want to do override checking, you write:

#include "override.h"
// ...
class B
{  
    // ...  
    OVERRIDE(A,void,foo,(int,double));  
    // ...
};

Now, when the signature of foo in A changes so that A::foo(int,double) does not exist anymore, you will get an error stating this. Also, if B suddenly isn't derived from A anymore, you will get an error.

Let's put this into practice in the example that I gave in the introduction. If you would have used my OVERRIDE macro, you would have written your original code like this:

class A
{
public:  
    virtual void foo()  
    {    
        std::cout << "A::foo" << endl;  
    }
};
class B : public A
{
public:  
    OVERRIDE(A,void,foo,())  
    {    
        std::cout << "B::foo" << endl;  
    }
};

int main()
{  
    A* a = new B;  a->foo(); // prints "B::foo".
}

Now let's see what would have happened if you'd made the same change that was made in the introduction, that is, if you added a parameter to A's foo function. You'd then get this code:

class A
{
public:  
    virtual void foo(int n = 3)  
    {    
        std::cout << "A::foo(" << n << ")" << endl;  
    }
};
class B : public A
{
public:  OVERRIDE(A,void,foo,())  
         {    
             std::cout << "B::foo" << endl;  
         }
};

int main()
{  
    A* a = new B;  a->foo(); // prints "B::foo".
}

If you hadn't used OVERRIDE, this would have compiled just fine, and you would only have noticed your mistake after running the program. Now however, due to the OVERRIDE macro, your compiler generates an error, alerting you to your mistake!

For completeness, I'll give you a list of the parameters of the macro:

  1. The base class whose member function we want to override
  2. The return type of the member function
  3. The name of the member function that we're overriding
  4. The list of parameters of the member function, in parentheses!

It is important to know that the checking does generate some code, and even though the code is never called, you might want to disable the checks when you're doing a release build. You can do this by defining WITHOUT_OVERRIDE_CHECKING in your project settings.

Caveats

The trickery that this macro pulls does not work on all compilers, unfortunately. The reason for this is that some compilers (the Comeau compiler, for instance) do not allow you to take the address of a member function that is protected or private. On these compilers, the checks will work fine for overriding public member functions, but they will not work for protected or private member functions. To get around this, you must define WITHOUT_OVERRIDE_CHECKING when using these compilers, so that the checks are not compiled.

History

  • Version 0.1, 24 October 2003: Initial release.
  • Version 0.2, 16 November 2003: Major overhaul, moved macro usage to class definition, added support for protected functions.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


Written By
Web Developer
Netherlands Netherlands
Bart Samwel is a professional C++ programmer. He is currently employed by Centric, a large IT company in The Netherlands. (Obligatory disclaimer: his articles here are a private matter, his employer has no responsibility for them.) Earlier, he has worked at Leiden University, where he has developed the Open Kernel Environment. Bart's home page can be found here.

Comments and Discussions

 
GeneralWorks brilliantly in practice Pin
David Pritchard23-Feb-04 0:06
David Pritchard23-Feb-04 0:06 
GeneralProblem with downloaded file(s). Pin
WREY17-Nov-03 9:02
WREY17-Nov-03 9:02 
GeneralRe: Problem with downloaded file(s). Pin
Bart Samwel17-Nov-03 21:34
Bart Samwel17-Nov-03 21:34 
GeneralWITHOUT_OVERRIDE_CHECKING not needed Pin
nadavba6-Nov-03 1:58
nadavba6-Nov-03 1:58 
GeneralRe: WITHOUT_OVERRIDE_CHECKING not needed Pin
Bart Samwel6-Nov-03 2:10
Bart Samwel6-Nov-03 2:10 
GeneralProblem and sub-optimal solution Pin
David Pritchard4-Nov-03 23:49
David Pritchard4-Nov-03 23:49 
GeneralRe: Problem and sub-optimal solution Pin
nadavba5-Nov-03 23:10
nadavba5-Nov-03 23:10 
GeneralRe: Problem and sub-optimal solution Pin
Bart Samwel6-Nov-03 0:05
Bart Samwel6-Nov-03 0:05 
GeneralRe: Problem and sub-optimal solution Pin
nadavba6-Nov-03 0:27
nadavba6-Nov-03 0:27 
GeneralRe: Problem and sub-optimal solution Pin
Bart Samwel6-Nov-03 1:09
Bart Samwel6-Nov-03 1:09 
GeneralRe: Problem and sub-optimal solution Pin
nadavba6-Nov-03 1:43
nadavba6-Nov-03 1:43 
GeneralRe: Problem and sub-optimal solution Pin
Bart Samwel6-Nov-03 2:08
Bart Samwel6-Nov-03 2:08 
GeneralRe: Problem and sub-optimal solution Pin
nadavba6-Nov-03 2:23
nadavba6-Nov-03 2:23 
GeneralRe: Problem and sub-optimal solution Pin
Bart Samwel6-Nov-03 3:06
Bart Samwel6-Nov-03 3:06 
GeneralRe: Problem and sub-optimal solution Pin
David Pritchard6-Nov-03 13:20
David Pritchard6-Nov-03 13:20 
GeneralYou OVERRIDE macro seems something verbose, maybe some improvement for it. Pin
_Chen_Jun_4-Nov-03 15:01
_Chen_Jun_4-Nov-03 15:01 
GeneralRe: You OVERRIDE macro seems something verbose, maybe some improvement for it. Pin
Bart Samwel4-Nov-03 22:15
Bart Samwel4-Nov-03 22:15 
GeneralThank you, but more to say. Pin
_Chen_Jun_5-Nov-03 14:33
_Chen_Jun_5-Nov-03 14:33 
GeneralRe: Thank you, but more to say. Pin
Bart Samwel5-Nov-03 23:29
Bart Samwel5-Nov-03 23:29 
GeneralAnother solution Pin
ZeroPageX29-Oct-03 8:12
ZeroPageX29-Oct-03 8:12 
GeneralRe: Another solution Pin
Bart Samwel29-Oct-03 10:56
Bart Samwel29-Oct-03 10:56 
GeneralRe: Another solution Pin
ZeroPageX29-Oct-03 11:02
ZeroPageX29-Oct-03 11:02 
GeneralRe: Another solution Pin
Andre Kaufmann30-Oct-03 8:25
Andre Kaufmann30-Oct-03 8:25 
GeneralRe: Another solution Pin
Bart Samwel30-Oct-03 10:43
Bart Samwel30-Oct-03 10:43 
GeneralRe: Another solution Pin
Andre Kaufmann31-Oct-03 3:20
Andre Kaufmann31-Oct-03 3:20 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.