2009-12-16 101 views
7

最近我遇到了一个令人沮丧的问题,它归结为一个非常简单的编码错误。请看下面的代码:缺少虚拟关键字的警告

#include <iostream> 

class Base 
{ 
public: 
    void func() { std::cout << "BASE" << std::endl; } 
}; 

class Derived : public Base 
{ 
public: 
    virtual void func() { std::cout << "DERIVED" << std::endl; } 
}; 

int main(int argc, char* argv[]) 
{ 
    Base* obj = new Derived; 
    obj->func(); 
    delete obj; 

    return 0; 
} 

输出是

BASE

显然,(这种情况),我的意思是,把虚拟关键字在基地:: FUNC使派生:: func将在main中调用。我意识到这可能是(可能)由C++标准允许的,可能有很好的理由,但在我看来,99%的时间这将是一个编码错误。但是,当我使用g ++和我能想到的所有-Wblah选项进行编译时,不会生成任何警告。

当派生类和派生类都具有相同名称的成员函数,而派生类的函数是虚拟的,而基类的函数不是虚函数时,是否有方法生成警告?

+0

'delete'的例子不能因为'Base'没有一个'virtual'析构函数 – 2012-09-28 20:02:50

回答

5

在Visual C++中,您可以使用override扩展名。像这样:

virtual void func() override { std::cout << "DERIVED" << std::endl; } 

如果该函数实际上没有覆盖基类方法,则会出现错误。我用这个为所有的虚拟功能。通常我定义一个宏是这样的:

#ifdef _MSC_VER 
#define OVERRIDE override 
#else 
#define OVERRIDE 
#endif 

所以,我可以这样使用它:

virtual void func() OVERRIDE { std::cout << "DERIVED" << std::endl; } 

我所期望的是G ++这样的事情也没有找到类似的概念。

我在Visual C++中唯一不喜欢它的地方是,你不能让编译器在所有重写的函数上都要求它(或至少是警告)。

+0

我用这个问题,以及破坏'Derived'。当有人更改函数的签名(添加参数等)但未能更新基准或派生对象时,它也很方便。通常情况下虚拟内存会静静地停止工作,但是这个技巧会让编译器产生一个错误。 – 2009-12-16 17:59:43

+1

太棒了!我不知道这个扩展。我可能会使用这个为我自己的项目,因为我使用Visual Studio,但对于这个特定的项目,我将不得不坚持g ++ :( – Jonesinator 2009-12-16 18:23:56

+2

请注意,'覆盖'是在C + + 11,所以这将工作最近在C++ 11模式下的gcc和clang('-std = C++ 11') – 2013-03-04 14:36:29

3

我不知道有任何g ++标志在此产生警告(不是说没有一个),但我会说这是一个非常罕见的错误。大多数人首先编写基类,作为使用纯虚函数的接口。如果你说过:

void func() = 0; 

那么你会得到一个语法错误。

1

人GCC

-Woverloaded虚拟(C++和Objective-C++只) 的函数时警告声明从基类隐藏虚函数。例如,在:

  struct A { 
      virtual void f(); 
      }; 

      struct B: public A { 
      void f(int); 
      }; 

    the "A" class version of "f" is hidden in "B", and code like: 

      B* b; 
      b->f(); 

    will fail to compile. 
+0

使用此选项不会在上面提到的代码中发出警告。在这种情况下,Foo :: func不是虚拟的(这是警告的先决条件),即使它仍然不会触发警告,因为Bar :: func匹配正确的签名。如果我创造了Foo :: func virtual并且只改变了Bar :: func的签名(即func(int)),那么就会触发警告。在我的情况下,我重写了Foo的非虚函数,这意味着如果我有Foo *,那么将调用Foo实现,而不是Bar实现。但是,只有当我有一个Bar *时才会调用Bar实现。 – Jonesinator 2011-10-30 00:18:36