2009-12-16 120 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