How to be sure a method is overriding an existing

2020-07-27 03:44发布

问题:

Let's suppose we have a base class which has a virtual method:

class BaseClass
{
    virtual void MethodToOverride() const
    {
        DoSomething();
    }
};

And a derived class which overrides the method (depending on the situation we can make it again virtual or not):

class DerivedClass : public BaseClass
{
    void MethodToOverride() const
    {
        DoSomethingElse();
    }
}

If we make a mistake, for example defining the MethodToOverride non const or with a wrong character, we simply define a new method, for example:

void MethodToOverride() {} // I forgot the const 
void MthodToOverride() const {} // I made a typo

So this compiles fine, but causes unwanted behavior at runtime.

Is there any way to define a function as an explicit override of an existing one, so the compiler warns me if I define it wrongly? Something like (I know it does not exist):

void MethodToOverride() const overrides BaseClass::MethodToOverride() const {} 

回答1:

C++0x offers an attribute for this (see vitaut's answer), and e.g. Visual C++ offers a language extension.

But in portable C++98 the best you can do is a sanity check, that the base class offers an accessible member function that accepts the same arguments, like ...

// The following macro is mainly comment-like, but performs such checking as it can.
#define IS_OVERRIDE_OF( memberSpec, args ) \
    suppressUnusedWarning( sizeof( (memberSpec args, 0) ) )

where

template< typename T >
inline void suppressUnusedWarning( T const& ) {}

You call the macro in your override implementation, with the function's actual arguments.

EDIT Added call example (disclaimer: untouched by compiler's hands):

class BaseClass
{
protected:
    virtual void MethodToOverride() const
    {
        DoSomething();
    }
};

class DerivedClass : public BaseClass
{
protected:
    void MethodToOverride() const
    {
        IS_OVERRIDE_OF( BaseClass::MethodToOverride, () );
        DoSomethingElse();
    }
};

Using such a sanity check can improve the clarity of the code in certain cases, and can save your ass in certain cases. It has three costs. (1) Someone Else might mistake it for a guarantee, rather than just an informative comment and partial check. (2) the member function can't be private in the base class, as it is in your example (although that's perhaps positive). (3) Some people react instinctively negatively to any use of macros (they've just memorized a rule about badness without understanding it).

Cheers & hth.,



回答2:

The best way is to declare the method to be pure virtual in BaseClass.

class BaseClass 
{ 
    virtual void MethodToOverride() const = 0;
};

If implementing classes are inherited again (which I would put in question as a semi good practice), there is no way to control the correct implementation.



回答3:

[[override]] attribute. However it is a part of C++0x.

If you are using gcc consider the -Woverloaded-virtual command-line option.



回答4:

If your base class may be an abstract one, then the solution is to make the methods you want to be overriden pure virtual. In this case the compiler will yell if you try to instantiate the derived class. Note that pure virtual functions can also have definitions.

E.g.

class BaseClass
{
    virtual void MethodToOverride() const = 0;
    //let's not forget the destructor should be virtual as well! 
};

inline void BaseClass::MethodToVerride const()
{
    DoSomething();
}
//note that according to the current standard, for some inexplicable reasons the definition
//of a pure virtual function cannot appear 'inline' in the class, only outside

If you cannot afford your base class to be abstract, then C++03 gives little to do and @vitaut's answer gives what you need for C++0x.

There was a sentence in your question which alarmed me. You say you can choose to make the method further virtual or not. Well, you can't, in C++03. If the method has been declared virtual it will be virtual throughout the hierarchy, whether you explicitly specify it or not. E.G.

class A
{
    virtual void f(){}
} ;
class B: public A
{
   void f(); //same as virtual void f();
};


回答5:

You can try this: :)

#include <iostream>

using namespace std;

class Base
{
    public:
        virtual void YourMethod(int) const = 0;
};

class Intermediate : private Base
{
    public:
        virtual void YourMethod(int i) const
        {
            cout << "Calling from Intermediate : " << i << "\n";
        }
};

class Derived : public Intermediate, private Base
{
    public:
        void YourMethod(int i) const
        {
            //Default implementation
            Intermediate::YourMethod(i);

            cout << "Calling from Derived : " << i << "\n";
        }
};

int main()
{
    Intermediate* pInterface = new Derived;
    pInterface->YourMethod(10);
}

I think the code speaks for itself. Base makes sure you implement the function with the correct signature (As a side effect makes you always implement it, even though you can use default behavior) while Intermediate which is the interface makes sure that there is a default implementation. However, you are left with a warning :).