【问题标题】:How to be sure a method is overriding an existing virtual one in C++?如何确定一种方法正在覆盖 C++ 中现有的虚拟方法?
【发布时间】:2011-05-06 06:37:32
【问题描述】:

假设我们有一个基类,它有一个虚方法:

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

还有一个覆盖该方法的派生类(根据情况我们可以将其再次设为虚拟或不虚拟):

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

如果我们犯了错误,例如定义 MethodToOverride 非 const 或使用错误的字符,我们只需定义一个新方法,例如:

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

所以这编译得很好,但在运行时会导致不需要的行为。

有没有办法将函数定义为对现有函数的显式覆盖,所以如果我定义错误,编译器会警告我?类似的东西(我知道它不存在):

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

【问题讨论】:

  • "(根据情况我们可以再次将其变为虚拟或不虚拟)" ?无论您是否将其实际标记为虚拟,该方法都将是虚拟的,所以我不太明白您的评论是什么意思。
  • 在得到一些非常好的答案后,我很难接受只有一个:我接受了 Alf 的答案,因为这是我正在寻找的技巧,但我也喜欢 Paul 的最佳风格投票和 Vitaut 的未来...谢谢大家

标签: c++ oop overriding virtual


【解决方案1】:

最好的办法是在BaseClass中声明方法是纯虚的。

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

如果实现类再次被继承(我认为这是一种半好的做法),则无法控制正确的实现。

【讨论】:

  • 这几乎是当前 C++ 中唯一的方法,但它只有在继承抽象基类时才有效,这是非常有限的......
  • 这是一个很好的方法,我肯定会推荐使用纯虚拟而不是简单的虚拟。永远不要从具体类继承(除非你知道确切为什么),而是总是从纯抽象类继承。简单的规则,但非常有效!
【解决方案2】:

[[override]] 属性。但是它是C++0x 的一部分。

如果您使用 gcc,请考虑 -Woverloaded-virtual 命令行选项。

【讨论】:

【解决方案3】:

C++0x 为此提供了一个属性(请参阅 vitaut 的答案),例如Visual C++ 提供语言扩展。

但在可移植的 C++98 中,您能做的最好的事情是进行健全性检查,即基类提供了一个可访问的成员函数,该函数接受相同的参数,例如 ...

// 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) ) )

在哪里

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

您在覆盖实现中调用宏,并使用函数的实际参数。

编辑添加调用示例(免责声明:编译器未触及):

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

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

在某些情况下使用这种健全性检查可以提高代码的清晰度,并且在某些情况下可以节省您的精力。它有三个成本。 (1) 其他人可能会将其误认为是保证,而不仅仅是信息性评论和部分检查。 (2) 成员函数在基类中不能是私有的,就像在您的示例中那样(尽管这可能是积极的)。 (3) 有些人本能地对任何宏的使用做出消极反应(他们只是记住了一条关于坏的规则而没有理解它)。

干杯,

【讨论】:

  • 你能在测试参数方面做得更好吗?也许通过将&amp;BaseClass::MethodToOverride 分配给(DerivedClass::*)(params_of_this_function)。我问是因为我怀疑使用错误的整数类型可能是无法覆盖函数的常见方法。
  • @Steve: 如果 C++98/03 有 typeof 会很简单...在调用中重复所有参数类型可能太冗长了?我的意思是,有很多奇特的解决方案可以解决极端情况问题,比如 Boost 的自动参数类型,它们之所以不被使用,仅仅是因为它输入太多,阅读和维护太多。战争故事,虽然是 Java:我曾经不得不帮助某人处理日期戳管理课程。首先给了她没有JavaDoc的版本。好的,谢谢,太好了,可以用这个,是的!然后是与 JavaDoc 相同的代码:Alf,我一点也不明白! T'只是增加的措辞。干杯,
  • 希望重复类型并不比重复宏现在的参数名称更难,但我同意平均来说有点困难。至于 Javadoc:这就是为什么 cmets 在我的语法高亮设置(黑色背景)或淡绿色(白色)中总是呈淡灰色。
【解决方案4】:

如果您的基类可能是一个抽象类,那么解决方案是使您想要被覆盖的方法成为纯虚拟的。在这种情况下,如果您尝试实例化派生类,编译器会大喊大叫。请注意,纯虚函数也可以有定义。

例如

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

如果你不能让你的基类变得抽象,那么 C++03 几乎没有什么可做的,@vitaut 的答案给出了你对 C++0x 的需求。

你的问题中有一句话让我感到震惊。您说您可以选择是否使该方法进一步虚拟化。好吧,在 C++03 中你不能。如果该方法已被声明为虚拟,则无论您是否明确指定它,它将在整个层次结构中都是虚拟的。例如

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

【讨论】:

    【解决方案5】:

    你可以试试这个:)

    #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);
    }
    

    我认为代码不言自明。 Base 确保您使用正确的签名实现函数(作为副作用,即使您可以使用默认行为,您也总是会实现它),而作为接口的Intermediate 确保存在默认实现。但是,您会收到警告:)。

    【讨论】:

      猜你喜欢
      • 2011-05-21
      • 2020-08-31
      • 2018-01-28
      • 2013-01-15
      • 1970-01-01
      • 2014-01-05
      • 2012-06-19
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多