多繼承中重寫不同基類中的虛函數

多繼承中重寫不同基類中的虛函數

C++多繼承體系當中,在派生類中可以重寫不同基類中的虛函數。

下面就是一個例子:

——————————————————————————————

例一:

class CBaseA

{

public:

     virtual void TestA();

};

class CBaseB

{

public:

     virtual void TestB();

};

class CDerived : public CBaseA, public CBaseB

{

public:

     virtual void TestA(); // 重寫基類CBaseA中的虛函數TestA()

     virtual void TestB(); // 重寫基類CBaseB中的虛函數TestB()

};

void Test()

{

     CDerived D;

     CBaseA *pA = &D;

     CBaseB *pB = &D;

     pA->TestA(); // 調用類CDerivedTestA()函數

     pB->TestB(); // 調用類CDerivedTestB()函數

}

——————————————————————————————

可是,如果兩個基類中有一個相同原型的虛函數,例如下面這樣:

例二:

class CBaseA

{

public:

     virtual void Test();

};

class CBaseB

{

public:

     virtual void Test();

};

     怎樣在派生類中重寫這兩個相同原型的虛函數呢?

     也許這種情況並不常見,可是這種情況卻確實存在。比如說開發的時候使用的兩個

類庫是不同的廠商提供的,或者說這兩個類庫是由公司

的不同開發小組開發的。對前者來說,修改基類的接口是不可能的;對後者來說,修改

接口的代價很大。

     如果在派生類中直接重寫這個虛函數,那麼2個基類的Test()虛函數都將被覆蓋。這

樣的話就只能有一個Test()的實現,而不是像前面的例

子那樣有不同的實現。

class CDerived : public CBaseA, public CBaseB

{

public:

     virtual void Test();

};

void Test()

{

     CDerived D;

     CBaseA *pA = &D;

     CBaseB *pB = &D;

     // 下面2行代碼都將調用類CDerivedTest()函數

     pA->Test();

     pB->Test(); 

}

——————————————————————————————

     爲了實現第一個例子中的那樣,在派生類CDerived中重寫不同基類中相同原型的虛

函數Test(),可以使用下面的方法。

     首先,不需要對2個基類進行任何修改(在實際的開發當中,修改基類的可能性非常小)

例三:

class CBaseA

{

public:

     virtual void Test();

};

class CBaseB

{

public:

     virtual void Test();

};

     現在,爲這個繼承體系添加2箇中間類,分別從2個基類派生。

class CMiddleBaseA : public CBaseA

{

private:

     // 真正的實現函數

     // 設置爲純虛函數,在派生類裏必須實現

     virtual void CBaseA_Test() = 0;

     // 改寫繼承下來的虛函數

     // 僅僅直接調用真正的實現函數

     virtual void Test() 

     { 

         CBaseA_Test(); 

     }

};

// 與類CMiddleBaseA採用相同的方法

class CMiddleBaseB : public CBaseB

{

private:

     virtual void CBaseB_Test() = 0;

     virtual void Test() 

     { 

         CBaseB_Test(); 

     }

};

     然後,類CDerived以上面2箇中間類作爲基類來派生。分別重寫上面2個基類中原型

不同的純虛函數,添加不同的實現代碼。

class CDerived : public CMiddleBaseA, public CMiddleBaseB

{

private:

     // 重寫從中間類繼承下來的虛函數

     virtual void CBaseA_Test(); // 這裏實際上是重寫CBaseATest()

     virtual void CBaseB_Test(); // 這裏實際上是重寫CBaseBTest()

};

void Test()

{

     CDerived D;

     CBaseA *pA = &D;

     CBaseB *pB = &D;

     // 調用類CBaseATest()函數

     // 由於C++多態的特性,實際上調用的是類CDervied中的CBaseA_Test()函數

     pA->Test(); 

     // 調用類CBaseBTest()函數

     // 由於C++多態的特性,實際上調用的是類CDervied中的CBaseB_Test()函數

     pB->Test();

}

     現在以上面代碼中的pA->Test();這行代碼來說明上面的方案是怎麼實現的。

     首先,由於虛函數Test()在類CBaseA的派生類CMiddleBaseA中被重寫,所以這行代

碼會去調用類CMiddleBaseATest()函數;

     然後,類CMiddleBaseATest()函數會去調用實現函數CBaseA_Test()

     最後,由於虛函數CBaseA_Test()在類CMiddleBaseA的派生類CDerived中被重寫,所

以真正調用的是類CDerived中的CBaseA_Test()函數。

     同樣的道理,代碼pB->Test();實際上調用的是類CDervied中的CBaseB_Test()函數。

     通過上面的方法就可以在C++多繼承中重寫不同基類中相同原型的虛函數。

另附一份解釋:

這是一個很好的問題!

我曾經輕視過它,但是現在我意識到這個問題其實很簡單,但是有些subtle

看以下的代碼:

class GrandParent

{

public:

virtual void kick()

{

// kick the Parent

}

};

class Parent

{

private:

virtual void kick()

{

// kick the children

}

};

class Child

{

protected:

virtual void kick()

{

// kick the dog

}

};

void test(void)

{

     GrandParent* p=new Child;

}

在這裏p->kick的絕對是dog。問題出在這裏:在public, protected, private等等權限只存

在於編譯時期,而不在運行時起。多態性則發生在運行時期而不在編譯時期。這樣,容易看

出,虛表的結構大致如下:

child structure:

vptr of Child

vptr of Parent

vptr of GrandParent

vtbl of Child:

ptr of Child::kick             // NO PUBLIC/PROTECT/PRIVATE ACCESS INFORMATION IN

VTBL!

vtbl of Parent:

ptr of Parent::kick

vtbl of GrandParent

ptr of GrandParent::kick

在編譯時期,因爲ptypeidGrandParent*,所以在使用p->kick時編譯器檢查

GrandParent中相應的內容,因爲p指針指向的是Children類產生的對象中的GrandParent

分,發現爲public。於是編譯通過。這是一個靜態的過程。

p->kick的代碼在編譯之後,因爲運行時是動態識別對象類型的,這樣因爲p指向的具體數據

Child類型的,所以在kick時傳遞的this指針的類型是Child*的,虛表結構中因爲對象的

類型是Child,這樣Child中的成員優先調用(此時沒有訪問權限檢查!!),然後就訪問了

Child*->kick

這就是這個問題的解決之道。其實我在看這個問題的時候,首先想到的是

class Derive

{

public:

virtual void test()

{

if (typeid(this)==typeid(ParentA*)) ...

if (typeid(this)==typeid(ParentB*)) ...

}

};

但是沒有用。因爲this的類型在動態解析時得到的是自身的類型。

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章