簡體   English   中英

C ++指向成員函數繼承的指針

[英]C++ Pointers to Member Functions Inheritance

我需要能夠擁有一個由繼承自它的類定義的超類執行回調。 我對C ++相對較新,從我可以看出它看起來像成員函數指針的主題是一個非常模糊的區域。

我已經看到了問題的答案和隨機博客文章討論了各種各樣的事情,但我不確定他們中是否有任何具體處理我的問題。

這是一段簡單的代碼,說明了我想要做的事情。 這個例子可能沒有多大意義,但它准確地類似於我想寫的代碼。

class A {
    protected:
    void doSomething(void (A::*someCallback)(int a)) {
        (*this.*someCallback)(1234);
    }
};

class B : public A {
    public:
    void runDoIt() { doSomething(&B::doIt); }
    void runDoSomethingElse() { doSomething(&B::doSomethingElse); }
    protected:
    void doIt(int foo) {
        cout << "Do It! [" << foo << "]\n";
    }
    void doSomethingElse(int foo) {
        cout << "Do Something Else! [" << foo << "]\n";
    }
};

int main(int argc, char *argv[]) {
    B b;
    b.runDoIt();
    b.runDoSomethingElse();
}

問題是B的成員函數不是A的成員函數,即使B來自A 如果你有一個void (A::*)() ,你可以在任何A *上調用它,而不管指向的對象的實際派生類型。

(當然,同樣的原則也適用於A & ..)

假設B來自A ,而C來自A ; 如果可以將void (B::*)() (例如)視為void (A::*)() ,可以做類似這樣的事情:

A *c=new C;
A *b=new B;
void (A::*f)()=&B::fn;//fn is not defined in A
(c->*f)();

並且將在C類型的對象上調用B的成員函數。 結果最多是無法預測的。

基於示例代碼,並假設不使用類似boost的東西,我傾向於將回調構造為一個對象:

class Callback {
public:
    virtual ~Callback() {
    }

    virtual Do(int a)=0;
};

然后調用回調的函數以某種方式獲取其中一個對象而不是普通的函數指針:

class A {
protected:
    void doSomething(Callback *c) {
        c->Do(1234);
    }
};

然后,您可以對每個您感興趣的派生函數進行一次回調。 對於doIt,例如:

class B:public A {
public:
    void runDoIt() {
        DoItCallback cb(this);
        this->doSomething(&cb);
    }
protected:
    void doIt(int foo) {
        // whatever
    }
private:
    class DoItCallback:public Callback {
    public:
        DoItCallback(B *b):b_(b) {}

        void Do(int a) {
            b_->doIt(a);
        }
    private:
        B *b_;
    };
};

削減樣板的一種顯而易見的方法是將成員函數指針放入回調中,因為派生的回調可以自由處理特定類型的對象。 這會使回調更通用,因為當回調它時會調用類型B的對象上的任意成員函數:

class BCallback:public Callback {
public:
    BCallback(B *obj,void (B::*fn)(int)):obj_(obj),fn_(fn) {}

    void Do(int a) {
        (obj_->*fn_)(a);
    }
private:
    B *obj_;
    void (B::*fn_)(int);
};

這將使得像這樣:

void B::runDoIt() {
    BCallback cb(this,&B::doIt);
    this->doSomething(&cb);
}

這可能會被“改進”,盡管不是所有讀者都可以通過模板化來看待它:

template<class T>
class GenericCallback:public Callback {
public:
    GenericCallback(T *obj,void (T::*fn)(int)):obj_(obj),fn_(fn) {}

    void Do(int a) {
        (obj_->*fn_)(a);
    }
private:
    T *obj_;
    void (T::*fn_)(int);
};

使用它,上面的runDoIt函數可能變為:

void B::runDoIt() {
    GenericCallback<B> cb(this,&B::doIt);
    this->doSomething(&cb);
}

(泛型回調也可以在成員函數指針本身上進行模板化,盡管在大多數情況下這不太可能提供任何實際優勢。它只是更多的輸入。)

我發現以這種方式構造事物結果很好,因為它不需要繼承。 因此它對要回叫的代碼幾乎沒有限制,這在我看來總是一件好事,而且我發現它超過了難以完全消除的冗長。 不可能基於這個例子來判斷這種方法是否真的適合,但......

如果你可以使用boost庫,我建議你使用boost :: function來完成手頭的任務。

class A {
public:
   void doSomething( boost::function< void ( int ) > callback )
   {
      callback( 5 );
   }
};

那么任何繼承(或外部類)都可以使用boost :: bind做一個調用:

class B {
public:
   void my_method( int a );
};
void test()
{
   B b;
   A a;
   a.doSomething( boost::bind( &B::my_method, &b, _1 ) );
};

我沒有檢查確切的語法,我從頭頂輸入它,但這至少接近正確的代碼。

為了您的目的,C ++為成員函數指針提供了更好的結構。 (一般來說,你不必在正確編寫的純C ++中使用函數指針)。 我頭頂的兩個選項:

1)在A上創建一個名為doIt()的純虛函數。 從doSomething()調用doIt()。 在B上,保留一個枚舉成員變量來指示你想要做什么,並在調用doSomething()之前設置它。 在B中覆蓋doIt(),其實現開關/將成員枚舉包含在您希望運行的底層代碼中。

2)使用單個doIt()純虛方法創建DoInterface類。 為你的類B的這個衍生物具有一個指針到擁有乙實例和實施doIt方法()調用乙相應的函數在每個DoInterface實現。 doSomething()將DoInterface的實例作為參數。 這稱為回調對象模式。

您試圖將派生類中定義的方法作為方法基類調用。
但是這個函數沒有在基類中定義。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM