2010-11-07 67 views
2

让我们假设我们有有一个虚拟方法的基类:如何确定一个方法覆盖C++中现有的虚拟方法?

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 {} 
+0

“(根据情况我们可以再次虚拟或不)”?该方法将是虚拟的,无论你实际上是否将其标记为虚拟,所以我不太明白你的意思。 – 2010-11-07 14:43:39

+0

是的,我意识到这是错误的 – martjno 2010-11-07 15:20:53

+0

得到一些非常好的答案后,我有一些困难,只接受一个:我接受了阿尔弗的一个,因为这是我寻找的伎俩,但我也喜欢保罗的最佳风格和Vitaut投票未来...感谢所有人 – martjno 2010-11-07 15:29:46

回答

1

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)有些人本能地对宏的使用产生了消极反应(他们只是记住了一个关于坏的规则而不理解它)。

干杯&第h。,

+0

您能否在测试参数时做得更好?也许通过给'(DerivedClass :: *)(params_of_this_function)'分配'&BaseClass :: MethodToOverride'。我问,因为我怀疑使用错误的整数类型可能是无法覆盖函数的常见方法。 – 2010-11-07 13:53:32

+0

@Steve:如果C++ 98/03有'typeof'会很简单...在调用中重复所有的参数类型可能太冗长了?我的意思是,有很多奇特的解决方案来解决角落案例问题,比如Boost的自动参数类型,这些类型并不仅仅是因为输入太多而太多太多的读取和维护。战争的故事,虽然Java:我曾经不得不帮助有日期戳管理类的人。首先给了她没有JavaDoc的版本。好的,谢谢,很好,可以使用这个,是的!然后与JavaDoc相同的代码:Alf,我完全不理解这个! T'只是附加的语言。干杯, – 2010-11-07 15:02:32

+0

希望重复这些类型并不比重复这些参数的名称要困难得多,但我同意它平均有点难度。至于Javadoc:这就是为什么我的语法高亮设置(黑色背景)或淡绿色(白色)时评论总是呈灰色。 – 2010-11-07 17:30:23

5

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

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

+1

它将成为C++ 0x中真正的关键字。他们发现属性对此没有任何好处。 – 2010-11-07 12:00:43

+0

它已经被一些编译器支持 - http://msdn.microsoft.com/en-us/library/z8ew2153(v=VS.90).aspx – 2010-11-07 12:07:19

0

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

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 

如果您不能负担你的基类是抽象的,那么C++ 03给出了不大和@ vitaut的答案给您所需要的的C++ 0x。

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

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

感谢您指定它,我没有把它解决。 – martjno 2010-11-07 15:24:01

6

的最好方法是声明为纯虚拟在BaseClass的方法。

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

如果实现类再次继承(我会把问题作为一个半好的做法),是没有办法控制的正确实施。

+0

这几乎是在当前C++中这样做的唯一方法,但它只适用于从抽象基类继承,这是相当有限的... – rubenvb 2010-11-07 12:16:35

+0

这是一个好方法,我肯定会推荐使用纯虚拟而不是简单的虚拟。永远不要从具体类继承(除非你完全知道*为什么),而是始终从纯粹的抽象类继承。简单的规则,但非常有效! – 2010-11-07 13:58:49

+0

.......................我如何删除评论? – nakiya 2010-11-07 18:59:18

0

你可以试试这个::)

#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确保存在默认实现。但是,你留下了一个警告:)。