2010-11-07 141 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確保存在默認實現。但是,你留下了一個警告:)。