2013-02-25 47 views
1

我有一個基類Base,它有方法runUserMethod(void * pointer_to_method)(C++)如何指向子類中的方法?

class Base { 
    int runUserMethod(METHTYPE pointer_to_method) { 
     // do some stuff before 
     int retval = pointer_to_method(); 
     // do some stuff after 
     return retval; 
    } 
}; 

現在,我想在某種程度上,它是能夠運行由Base一個子類實現的任何方法,但這種方法遵循一些標準簽名以實現runUserMethod,如int <methodname> (void)

例如,假設用戶創建像類UserClass

class UserClass : public Base { 
    int user_method (void); 
}; 

我應該METHTYPE是,這樣我可以做到以下幾點?

Base * b = new UserClass(); 
int retval = b->runUserMethod(&UserClass::user_method); 

此外,它會工作嗎?有沒有更好的方法來做到這一點?基本上,我試圖做的是攔截調用子類的方法調用,或多或少透明的方式給用戶。

+0

你是知道的,一個'無效*'和成員函數指針不一定是相同的尺寸(和最經常都沒有),這是在他們之間鑄造往往會失敗(除了它主要是UB反正)? – PlasmaHH 2013-02-25 16:51:00

+0

只是做'UserClass()。user_method()'。忘記'runUserMethod'和派生類的所有複雜性以及什麼。調用成員函數是微不足道的。 – 2013-02-25 17:01:37

+0

@ Cheersandhth.-Alf'UserClass'和'user_method'事先不知道......我使用'runUserMethod'函數截取對子類方法的調用。 – 2013-02-25 17:08:05

回答

1

你可以嘗試製作runUserMethod模板方法。另外,爲了安全地從基礎施法派生你應該多態使用的dynamic_cast所以基地類必須類(通過添加例如虛擬析構函數):

class Base { 
public: 

    template <class T> 
    int runUserMethod(int (T::* pointer_to_method)()) { 
     T* p = dynamic_cast<T*> (this); 
     if (p == NULL) throw std::runtime_error("cast error"); 


     // do some stuff before 
     int retval = (p->*pointer_to_method)(); 
     // do some stuff after 
     return retval; 
    } 

    virtual ~Base() {}; 
}; 

函數的用法是一樣的您的代碼示例:

Base* b = new UserClass; 
int r = b->runUserMethod(&User::user_method); 

函數參數和返回值類型也可以做成模板參數,使runUserMethod接受其他函數簽名:

class Base { 
public: 

    // 0 arguments 
    template <class T, class R> 
    int runUserMethod(R (T::* pointer_to_method)()) { 
     T* p = dynamic_cast<T*> (this); 
     if (p == NULL) throw std::runtime_error("cast error"); 

     // do some stuff before 
     R retval = (p->*pointer_to_method)(); 
     // do some stuff after 
     return retval; 
    } 

    // 1 argument 
    template <class T, class R, class ARG1> 
    int runUserMethod(R (T::* pointer_to_method)(ARG1), ARG1 arg1) { 
     T* p = dynamic_cast<T*> (this); 
     if (p == NULL) throw std::runtime_error("cast error"); 

     // do some stuff before 
     R retval = (p->*pointer_to_method)(arg1); 
     // do some stuff after 
     return retval; 
    } 

    // 2 arguments 
    // [...] 

    virtual ~Base() {}; 
}; 

下面這樣的代碼也將工作:

class UserClass : public Base { 
public: 
    int user_method1 (void) {return 0;} 
    float user_method2 (void) {return 0.0f;} 
    int user_method3 (int x) {return 0;} 
}; 

int main(int argc, char* argv[]) { 
    Base* b = new UserClass; 
    int r1 = b->runUserMethod(&UserClass::user_method1); 
    float r2 = b->runUserMethod(&UserClass::user_method2); 
    int r3 = b->runUserMethod(&UserClass::user_method3, 2); 
} 
+0

這太棒了!這正是我期待的!爲了完整性,您可以使用模板參數和返回類型來擴展您的答案嗎?另外,這種做事方式有多安全? – 2013-02-26 09:10:55

+0

動態投射將保護您免受@Slava描述的同胞問題。您可能遇到模板推導的類型問題。例如,如果'usermethod3'接受'int&'而不是'int'並且你正在傳遞一個文字常量,那麼代碼將不會被編譯。但是這些問題可以通過[Boost.TypeTraits](http://www.boost.org/doc/libs/1_53_0/libs/type_traits/doc/html/index.html)消除(本例中的'remove_reference') 。 – 2013-02-26 13:47:10

2

這可以通過使用std::functionstd::bind來完成:

struct Base 
{ 
    typedef std::function<int()> METHTYPE; 

    int runUserMethod(METHTYPE pointer_to_method) 
    { 
     return pointer_to_method(); 
    } 
}; 

UserObject b; 
auto func_object = std::bind(&UserObject::user_method, std::ref(b)); 

int retval = b.runUserMethod(func_object); 
+1

請注意,這綁定到'b'的副本。你通常需要''b'或'std :: ref(b)'作爲'bind'的參數,以便綁定到'b'本身。 – 2013-02-25 17:08:32

+0

感謝您的快速回復,Joachim。然而,是否有可能以某種方式封裝這些額外的代碼行(用綁定,我的意思是)。我希望用戶只需運行'obj-> runUserMethod(SOMETHING)'而不是'obj-> SOMETHING()'。 – 2013-02-25 17:33:21

+0

@EduardoBezerra要調用一個非靜態成員函數,需要_both_對象實例和函數。沒有辦法繞過它。 – 2013-02-26 05:46:38

0

如果改變更真實可行的狀態,你的例子中,你將看到的問題:

int retval  = (this->*methptr)(); 

撇開醜陋的語法,問題是您需要將具有類型Base *的「this」指針傳遞給方法調用,但您應該將指針傳遞給派生類的方法
因此,您需要使runUserMethod知道派生類SS(可能是模板)或強制轉換指針衍生方法指向與風險的基礎方法的問題:

class Derived1 : public Base ... 
class Derived2 : public Base ... 
Derived2 d2; 
d2.runUserMethod(static_cast<METHTYPE>(&Derived1::func)); // problem Derived2 * will be passed to Deriived1 method and there is no easy way to detect that 

因此,使用的std ::綁定會更安全。你不必從基地繼承(我不知道你的情況是否好)

相關問題