1. 程式人生 > >C++筆記 第五十四課 被遺棄的多重繼承(下)---狄泰學院

C++筆記 第五十四課 被遺棄的多重繼承(下)---狄泰學院

如果在閱讀過程中發現有錯誤,望評論指正,希望大家一起學習,一起進步。
學習C++編譯環境:Linux

第五十四課 被遺棄的多重繼承(下)

1.多重繼承的問題三

多重繼承可能產生多個虛擬函式表
在這裡插入圖片描述

54-1 多重繼承問題三

#include <iostream>
#include <string>
using namespace std;
class BaseA
{
public:
    virtual void funcA()
    {
	cout << "BaseA::funcA()" << endl;
    }
};
class BaseB
{
public:
    virtual void funcB()
    {
	cout << "BaseB:: funcB()" << endl;
    }
};
class Derived : public BaseA, public BaseB
{
};
int main()
{
    Derived d;
    BaseA* pa = &d;
    BaseB* pb = &d;
    BaseB* pbe = (BaseB*)pa; //oops 不用這種方法
    BaseB* pbc = dynamic_cast<BaseB*>(pa);  //use this
    cout << "sizeof(d) = " << sizeof(d) << endl;
    cout << "Using pa to call funcA()..." << endl;
    pa->funcA();
    cout << "Using pb to call funcB()..." << endl;
    pb->funcB();
   
    cout << "Using pbb to call funcB()..." << endl;
    pbc->funcB();
    cout << endl;
    cout << "pa = " << pa << endl;
    cout << "pb = " << pb << endl;
    cout << "pbe = " << pbe << endl;
    cout << "pbc = " << pbc << endl;
    return 0;
}
執行結果
sizeof(d) = 16
Using pa to call funcA()...
BaseA::funcA()
Using pb to call funcB()...
BaseB:: funcB()
Using pbb to call funcB()...
BaseB:: funcB()
pa = 0x7fff843450d0
pb = 0x7fff843450d8
pbe = 0x7fff843450d0
pbc = 0x7fff843450d8

需要進行強制型別轉換時,C++中推薦使用新式型別轉換關鍵字!!
解決方案:dynamic_cast
在這裡插入圖片描述
程式執行本質:
通過pa呼叫funcA的過程:從pa裡面拿到pa的地址,通過地址找到虛擬函式表指標vptr1,進而通過虛擬函式表指標找物件的函式地址,進而找到funA的地址。
通過pb呼叫funcB的過程:從pb裡面拿到pb的地址,通過地址找到虛擬函式表指標vptr2,進而通過虛擬函式表指標所指向的虛擬函式表裡面得到虛擬函式地址,進而找到funB的地址。
vptr1,vptr2兩個虛擬函式表指標所指向的虛擬函式表在結構上是一樣的,因此通過pbb呼叫funcB的過程:首先得到地址,得到的地址在pbb所指向的位置,然後找虛擬函式表指標,找到的虛擬函式表指標vptr1,就在pbb所指向的虛擬函式表裡面找funcB()的地址,在這裡面是找不到funcB()的地址,能夠找到的是funcA()的地址,並且這個地址是完全正確沒有誤差的。

2.正確的使用多重繼承

工程開發中的“多重繼承”方式:
單繼承某個類 + 實現(多個)介面
在這裡插入圖片描述

54-2 正確的多繼承方式—單繼承、多介面進行面向物件的設計

#include <iostream>
#include <string>
using namespace std;
class Base
{
protected:
    int mi;
public:
    Base(int i)
    {
	mi = i;
    }
    int getI()
    {
	return mi;
    }
    bool equal(Base* obj)
    {
	return (this == obj);
    }
};
class Interface1
{
public:
    virtual void add(int i) = 0;
    virtual void minus(int i) = 0;
};
class Interface2
{
public:
    virtual void multiply(int i) = 0;
    virtual void divide(int i) = 0;
};
class Derived : public Base, public Interface1, public Interface2
{
public:
    Derived(int i) : Base(i)
    {
    }
    void add(int i)
    {
	mi += i;
    }
    void minus(int i)
    {
	mi -= i;
    }
    void multiply(int i)
    {
	mi *= i;
    }
    void divide(int i)
    {
	if( i!= 0)
        {
	    mi /= i;
        }
    }
}; 
int main()
{
    Derived d(100);
    Derived* p = &d;
    Interface1* pInt1 = &d;
    Interface2* pInt2 = &d;
    cout << "p->getI() = " << p->getI() << endl;  //100
    pInt1->add(10);    //110
    pInt2->divide(11); //10
    pInt1->minus(5);   //5
    pInt2->multiply(8);//40
    cout << "p->getI() = " << p->getI() << endl;  //40
    cout << endl;
    cout << "pInt1 == p : " << p->equal(dynamic_cast<Base*>(pInt1)) << endl;
    cout << "pInt2 == p : " << p->equal(dynamic_cast<Base*>(pInt2)) << endl;
    return 0;
}
執行結果
p->getI() = 100
p->getI() = 40
pInt1 == p : 1
pInt2 == p : 1

一些有用的工程建議—非常有用的技巧
先繼承字一個父類,然後實現多個介面
父類中提供equal()成員函式
equal()成員函式用於判斷指標是否指向當前物件
與多重繼承相關的強制型別轉換用dynamic_cast完成
小結
多繼承中可能出現多個虛擬函式表指標
與多重繼承相關的強制型別轉換用dynamic_cast完成
工程開發中採用“單繼承多介面”的方式使用多繼承
父類提供成員函式用於判斷指標是否指向當前物件