2008-09-11 107 views
3

雖然重構了一些傳統的C++代碼,但我發現我可能會通過某種方式刪除一些代碼重複,從而定義一個變量,該變量可指向任何共享相同簽名的類方法。一個小挖之後,我發現我可以做類似如下:指向C++類方法的指針

class MyClass 
{ 
protected: 
    bool CaseMethod1(int abc, const std::string& str) 
    { 
     cout << "case 1:" << str; 
     return true; 
    } 

    bool CaseMethod2(int abc, const std::string& str) 
    { 
     cout << "case 2:" << str; 
     return true; 
    } 

    bool CaseMethod3(int abc, const std::string& str) 
    { 
     cout << "case 3:" << str; 
     return true; 
    } 

public: 
    bool TestSwitch(int num) 
    { 
     bool (MyClass::*CaseMethod)(int, const std::string&); 

     switch (num) 
     { 
      case 1: CaseMethod = &MyClass::CaseMethod1; 
        break; 
      case 2: CaseMethod = &MyClass::CaseMethod2; 
        break; 
      case 3: CaseMethod = &MyClass::CaseMethod3; 
        break; 
     } 

     ... 

     bool res = CaseMethod(999, "hello world"); 

     ... 

     reurn res; 
    } 
}; 

我的問題是 - 這是正確的方式去嗎?我應該考慮Boost必須提供的任何東西嗎?

編輯...

好了,我的錯 - 我要調用該方法,像這樣:

bool res = ((*this).*CaseMethod)(999, "Hello World"); 

回答

8

你在那裏有一個指向成員函數的指針。它會解決你的問題。我很驚訝你的「TestSwitch」函數編譯,因爲調用語法與你所期望的略有不同。它應該是:

bool res = (this->*CaseMethod)(999, "hello world"); 

然而,你可能會發現的boost ::功能的組合和boost ::綁定使事情變得更容易一些,因爲可以避開奇異的調用語法。

boost::function<bool(int,std::string)> f= 
    boost::bind(&MyClass::CaseMethod1,this,_1,_2); 

當然,這將其綁定到當前this指針:可以使this指針的成員函數明確的第三個參數,如果你喜歡:

boost::function<bool(MyClass*,int,std::string)> f= 
    boost::bind(&MyClass::CaseMethod1,_1,_2,_3); 

另一種方法是,以使用虛函數和派生類,但這可能需要對代碼進行重大更改。

1

你當然可以做到這一點,雖然案例教學法調用不正確(這是一個指向成員函數的指針,所以你必須指定方法應該被調用的對象)。正確的調用是這樣的:

bool res = this->*CaseMethod(999, "hello world"); 

在另一方面,我建議boost::mem_fn - 你可以少機會搞砸了。 ;)

0

這裏給出的本地化示例沒有任何內在錯誤,但是如果您在更廣泛的上下文中使用類方法指針,那麼保持「安全」通常會非常棘手,例如在類外部,它們是指針或與複雜的繼承樹一起使用。編譯器通常管理方法指針的方式與「普通」指針不同(因爲除了代碼入口點外,還有額外的信息),因此對可以用它們執行的操作有很多限制。

如果你只是按照你描述的方式保持簡單的指針,那麼你會好起來的,但是前面更復雜的用途你可能想看看更廣義的仿函數系統,如boost::bind。這些可以將指針指向任何可調用的代碼指針,並且還可以根據需要綁定實例化的函數參數。

1

我沒有看到你的調用和簡單地調用switch語句中的方法之間的區別。

不,沒有語義或可讀性差異。

我看到的唯一區別是您正在使用指向某個方法的指針,因此禁止編譯器將其內聯或優化對該方法的任何調用。

3

你也可以建立一個查詢(如果您的鍵程合理),這樣寫出來:

this->*Methods[num](999, "hello world"); 

這消除了開關,以及,使得清理更有價值一點。

1

如果沒有更廣泛的背景下,很難找出正確的答案,但我在這裏縫三種可能性:

  • 住宿與正常switch語句,沒有必要做任何事情。這是最可能的解決方案

  • 使用指向與數組結合使用的成員函數指針,如@Simon所說,或者可能帶有映射。對於有大量案例的案例陳述,這可能會更快。

  • 將他的類拆分成多個類,每個類攜帶一個函數來調用,並使用虛函數。這可能是最好的解決方案,購買它需要一些嚴肅的反駁。考慮GoF模式,如國家或訪問者等。

0

有可用的其它方法,如使用一個抽象基類,或專門的模板函數。

我將描述基類的想法。

您可以定義一個抽象基類

class Base { virtual bool Method(int i, const string& s) = 0; }; 

然後寫出你的每一個案件作爲一個子類,如

class Case1 : public Base { virtual bool Method(..) { /* implement */; } }; 

在某些時候,你會得到你的「民」變量指示要執行的測試。你可以編寫一個帶有這個num的工廠函數(我將其稱爲which_case),並返回一個指向Base的指針,然後從該指針調用Method。

Base* CreateBase(int which_num) { /* metacode: return new Case[which_num]; */ } 
// ... later, when you want to actually call your method ... 
Base* base = CreateBase(23); 
base->Method(999, "hello world!"); 
delete base; // Or use a scoped pointer. 

順便說一句,這個應用程序使我希望C++支持靜態虛擬函數,或像「類型」爲內建類型 - 但事實並非如此。