2013-07-19 161 views
2

指針指向派生類指向指針指向基類的方法是合法的,即使基類沒有聲明任何方法,特別是的「鑄造」的方法是通過型基類的對象被調用,如follows將指向派生類的方法的指針投射到指向基類的方法的指針

// works in VS 2008 and g++ 4.5.3 
struct Base 
{ 
}; 

struct Fuu : public Base 
{ 
    void bar(){ std::cout << "Fuu::bar" << std::endl; } 
    void bax(){ std::cout << "Fuu::bax" << std::endl; } 
}; 

struct Foo : public Base 
{ 
    void bar(){ std::cout << "Foo::bar" << std::endl; } 
    void bax(){ std::cout << "Foo::bax" << std::endl; } 
}; 

typedef void (Base::*PtrToMethod)(); 

int main() 
{ 
    PtrToMethod ptr1 = (PtrToMethod) &Foo::bax; 
    PtrToMethod ptr2 = (PtrToMethod) &Fuu::bax; 

    Base *f1 = new Foo; 
    Base *f2 = new Fuu; 

    (f1->*ptr1)(); 
    (f2->*ptr2)(); 
} 
+1

對於「普通」指針,該規則被稱爲「協方差」:您可以將「派生*」轉換爲「基本*」(因爲「派生」是「基本」),而不是相反方向(因爲「 Base *'不一定指向'Derived')。對於_pointers-to-member_,這個規則被稱爲「contravariance」,並且基本上是與之相反的:您可以將'T Base :: *'轉換爲'T Derived :: *'(因爲'Base'的成員也是存在於一個Derived'對象中)而不是其他方式(因爲'Derived'可以將成員添加到'Base'中)。另外_please_不使用C風格演員:他們不安全,不清楚,難以發現。 –

+0

精細打印:您的示例雖然編譯和「有效」,但可能不合法。或者將'Base *''f1'強制轉換爲'Foo *'(如果你確定的話''static_cast'',如果你不需要''dynamic_cast''但是它需要'Base'至少有一個'virtual'函數] ),或者添加虛擬功能到'Base'的公共接口。 –

回答

5

值得注意的是,目標對象是逆變的原因是因爲this實際上是一個傳遞給函數的參數,理論上,參數是逆變的(如果函數可以使用Base*,它可以安全地插入轉換成僅提供Derived*作爲實際參數的任何算法)。

但是,對於任意參數,如果基礎子對象未放置在派生類佈局的開始位置,則可能需要使用填充來調整指針。使用指向成員的指針,this指針的指針調整內置在語言中。 (由於這個原因,指向虛擬繼承類的成員可能變得非常大)

+0

我總是用託尼女高音的格言:錢漲了,狗屎跑下坡了。在這種情況下:向下轉換參數很麻煩,向上轉換是很自然的。 – TemplateRex

+0

我理解成員函數的「暗示'這個'參數」推理(「問題」提到的「方法」),但爲了完整性,它能否擴展到成員_variables_? –

+2

@gx_:從理論上看,指向成員變量的指針*是一個函數,它將強類型對象地址映射到強類型子對象地址。在存在虛擬繼承的情況下,這些函數被限定爲方法,因爲它們是動態分派的。在大多數實現中,從不使用實際函數調用,子對象訪問代碼直接插入到使用位置,但這與內聯任何其他函數沒有區別。 –

2

這是在標準的第4.11(描述我有n3337.pdf草案):

「類型cv T的B的成員的指針」的類型的值,其中B是 類類型,可以被轉換爲類型爲「指向類型cv T的D的成員 」的類型的值,其中D是B的派生類(條款10)。如果B 是D的基類或D的虛基類的基類,則不可訪問(條款11),含糊(10.2)或虛擬(10.1) 需要此轉換的程序不合格。 的結果轉換指的是在轉換髮生之前指向成員 的指針相同的成員,但它指的是基類 成員,就好像它是派生類的成員一樣。結果在D的實例B中將成員 引用到成員。由於結果具有類型「指針 到類型cv T的成員D」,因此可以用D對象取消引用。 結果是一樣的,如果指針B的構件被解除引用與D的乙子對象空構件指針值 被轉換爲目的地 type.57

的空構件指針值

通常,指向成員轉換的指​​針在相反方向工作,然後指向派生/基類。指向(子)對象的指針可以轉換爲基類,指向方法的指針可以轉換爲更多的派生類。

請注意,如果您涉及的對象不是Foo/Fuu類型或其派生類型時嘗試通過上述任何指針進行調用,則您的程序會生病。

雖然我相信你的代碼冒着風險,它看起來像類似的轉換(即使沒有涉及繼承),在某些時候,它在Borland的OWL 2.0庫中被大量使用。

0

轉換指針是合法的。爲了使用它,你必須把它轉換回原來的類型。根本問題是指向函數的指針指向派生類的成員;無法保證該成員是基類的成員。

相關問題