2017年5月26日 星期五

多重繼承時函式名稱一樣怎麼重載虛擬函式

多重繼承時函式名稱一樣怎麼重載虛擬函式

函式名稱的衝突

繼承重要的用途之一就是
利用父類別的指針型別來建立子類別
Fa* p = new SonA;
p->fun();
如此一來父類別可以提供預設的行為,子類別也可以視需求更改行為或增加資料成員。
宣告都用父類別用途意旨fun()可以接收所有子類,這讓程式設計得到很大彈性。
但是在多重繼承的時候如果兩個不同的父類別名稱一樣會造成”曖昧”衝突,並且子類別沒辦法重新指定函式行為。
// Base class
class A1 {
public:
    virtual void draw(){cout << "A1" << endl;}
};
class A2{
public:
    virtual void draw(){cout << "A2" << endl;}
};
現在同時繼承他們
class A: public A1, public A2 {
public:
    void draw(){cout << "A draw" << endl;}
};
你的 draw() 到底是重寫 A1::draw() 還是 A2::draw()
沒有方法區別


新增中介轉發類別

我們可以繞個彎使用一對class重新導向函式名稱
class AuxA1: public A1{
public:
    virtual void A1draw() = 0;
    virtual inline void draw(){A1draw();}
};
class AuxA2: public A2{
public:
    virtual void A2draw() = 0;
    virtual inline void draw(){A2draw();}
};
現在,你繼承 AuxA1 重寫新的 A1draw() 就等於重載 A1 的 draw()
// 重載個別的 draw()
class A: public AuxA1, public AuxA2 {
public:
    void A1draw(){cout << "Override A1 draw" << endl;}
    void A2draw(){cout << "Override A2 draw" << endl;}
};
你可以放心的使用他們
A1* a1 = new A;
A2* a2 = new A;
a1 -> draw();
a2 -> draw();


虛擬函式的彈性

另外重申一下虛擬繼承的好處,像這樣的 funtion

class B{};
class D1: public B{};
class D2: public B{};

void fun(B* p){
    p->fun();
}
你可以根據輸入的指針不一樣選擇不一樣的行為
B* b1 = nullptr

b1 = new D1;
fun(b1); // 呼叫 D1 fun
b1 = new D2;
fun(b2); // 呼叫 D2 fun


範例代碼

/*****************************************************************
Name : 多重繼承時函式名稱一樣怎麼重載虛擬函式
Date : 2017/05/21
By   : CharlotteHonG
Final: 2017/05/26
*****************************************************************/
#include <iostream>
using namespace std;

// Base class
class A1 {
public:
    virtual void draw(){cout << "Base A1 draw" << endl;}
};
class A2{
public:
    virtual void draw(){cout << "Base A2 draw" << endl;}
};
//----------------------------------------------------------------
// 沒辦法重載個別的 draw()
// class A: public A1, public A2 {
// public:
//     void draw(){cout << "A draw" << endl;}
// };
//----------------------------------------------------------------
// 轉發 (明白的指定轉發函式)
class AuxA1: public A1{
public:
    virtual void A1draw() = 0;
    virtual inline void draw(){A1draw();}
};
class AuxA2: public A2{
public:
    virtual void A2draw() = 0;
    virtual inline void draw(){A2draw();}
};
// 重載個別的 draw()
class A: public AuxA1, public AuxA2 {
public:
    void A1draw(){cout << "Override A1 draw" << endl;}
    void A2draw(){cout << "Override A2 draw" << endl;}
};
//----------------------------------------------------------------
int main() {
    // 繼承的使用方式
    A1* a1 = new A;
    A2* a2 = new A;
    a1 -> draw();
    a2 -> draw();
    // 這樣仍然會因為曖昧不能使用
    // A a;
    // a.draw();
    return 0;
}
//----------------------------------------------------------------

沒有留言:

張貼留言