2012-02-04 41 views
3

我想要做的是有點模糊,所以讓我給上一個例子(這不是實際的代碼):類型轉換方法指針以父類的方法

template <class T> 
class ArrayStorage { 
protected: 
    void processStuff(void (ArrayStorage<T>::*procedure)(T *)) { 
     for (int i = 0; i < count; i++) 
      (this->*procedure)(content[i]); 
    } 
    // No method of type void (ArrayStorage<T>::*)(T *) 

private: 
    T **content; 
    int count; 
}; 

class DrawableStorage : public ArrayStorage<Drawable> { 
public: 
    void drawStuff() { 
     processStuff((void (ArrayStorage<Drawable>::*)(Drawable *)) &DrawableStorage::drawOne); 
    } 

private: 
    void drawOne(Drawable *item) { 
     item->draw(); 
    } 
}; 

基本上,有一個通用的容器,它能夠迭代它的項目並在每個項目上使用一個方法(其指針在參數中)。然而,這個方法並不存在於這個類中,而只存在於它的子類中。你可以看到,在子類「drawStuff」方法中,我提供了一個子類的方法指針,但是作爲基類的一個方法進行了類型化。

令人驚訝的是,這已經成功地編譯和工作得很好。

我的問題是,它只是一個巧合,我特別的編譯器能夠處理它,而實際上它是完全無效的,我應該擺脫它,或者是方法指針的正確使用?

謝謝。

回答

1

它根據$ 5.2合法的。9/12:

類型的「指針類型CV1的T d的部件」 A prvalue可以是 轉換成類型的prvalue「指針B的成員」類型CV2 T的 其中B是D的基類(第10項),如果有效標準 從「指向類型T的B成員的指針」轉換爲「指向類型T的D的成員的 成員」(4.11),並且cv2是相同的 cv-quali fi cation作爲,或更大的cv-quali fi cation比,cv1。 69空值 成員指針值(4.11)被轉換爲目標類型值的空成員指針 。如果B類包含原始成員 ,或者是包含 原始成員的類的基類或派生類,則生成的成員指針指向原始成員 。否則,演員的結果是不確定的。 [注意: 雖然B類不需要包含原始成員,但是指向成員的指針所對象的動態 類型必須包含原始成員 ;見5.5。末端注]

但是,如果你嘗試用多重繼承或虛擬繼承要做到這一點,這就是當事情開始打破,因爲不是所有的編譯器實現符合標準的方式成員函數指針。在MSVC和英特爾編譯器上,所有成員函數指針的大小不一樣,因此您將失去關於轉換的重要信息。

3

這是可能的指針的招數之一。它的工作原理是因爲DrawableStorage是ArrayStorage,函數頭相匹配的期望是什麼(需要一個指針類型的匹配對象模板類型並返回void)。

一旦父對象具有指向該函數的指針,即使它實際上並沒有將該函數作爲其自身的成員,它也可以調用它。

雖然它可能是一開始有點混亂閱讀,它是函數指針的一個非常有效的使用。

0

基類沒有請求的成員函數,所以沒有辦法解決這個問題。但是,可以使基礎功能的模板代替:

template <typename S> 
void processStuff(S * s, void (S::*procedure)(T *)) 
{ 
    for (int i = 0; i < count; i++) { (s->*procedure)(content[i]); } 
} 

調用它,如下所示:

processStuff(this, &DrawableStorage::drawOne); 
+0

是的,我想到了,但我想知道我的版本是否也有可能。 – Detheroc 2012-02-04 16:33:58

+0

@Dheheroc:不像你寫的那樣。如果你在基類中有一個虛擬接口,那麼也許,但是爲什麼這種方法是完全可用的呢?整個函數調用可能會被內聯,並且不會導致任何實際的代碼。 – 2012-02-04 16:36:34

1

這裏是你的榜樣的簡單版本:

#include <iostream> 
using namespace std; 

struct Arg 
{ 
    int i; 
}; 

class A; 
typedef void (A::*fncptr)(Arg&); 

fncptr是指針A類的成員函數(方法),其通過引用,採用Arg。如果有人給你提供這種類型的非靜態函數的地址,那麼只有當你有一個類A的對象(否則它必須是靜態函數)時,你才能夠調用它。

class A 
{ 
public: 
    A(Arg a) : a(a) { } 
    void process(fncptr f){ (this->*f)(a); } 

protected: 
    Arg a; 
}; 

class B : public A 
{ 
public: 
    B(Arg a) : A(a) { } 
    void magic(){ process((fncptr)&B::function); } 
    void function(Arg& a) { cout << a.i; } 
}; 

這裏的竅門:因爲你是在功能processA類的成員函數,你不需要指針A類的對象調用函數f,因爲你已經有一個:this。所以你可以調用這個類或派生類的任何函數,你只需要該函數的地址就可以這樣做。

這裏的main()

int main() 
{ 
    Arg a; 
    a.i = 71; 

    B* b = new B(a); 
    b->magic(); 
    delete b; 
} 

輸出:71