C++多繼承、多態(tài)、運行階段類型識別、四種類型轉(zhuǎn)換
#include <iostream>
using namespace std;
//C++允許多繼承,當子類繼承多個父類時需要解決父類之間成員名沖突
class ClassBaseA
{
????public:
???? ClassBaseA(int a = 0) :a(a) {}
???? int a;
???? void func() {}
};
class ClassBaseB
{
????public:
???? ClassBaseB(int a = 0) :a(a) {}
???? int a;
???? void func() {}
};
class ClassDeriveA:public ClassBaseA, public ClassBaseB //每個父類要分別寫訪問權(quán)限,父類之間逗號分隔
{
????public:
???? ClassDeriveA(int Aa = 0,int Ba=0)?
???? ???? :ClassBaseA(Aa), ClassBaseB(Ba) {} //每個父類都要初始化,如不使用默認無參構(gòu)造器就需要顯式指定
???? void invoke()?
???? {
???????? //func(); //二義性
???????? ClassBaseA::func(); //需要指明名稱空間
???????? ClassBaseB::func();
???????? ClassBaseB::a;
???????? ClassBaseA::a;
???? }
};
//當子類的多個基類繼承了同一個基類時,會產(chǎn)生多份同樣的成員,通過虛繼承解決
class ClassDerive1:virtual public ClassBaseA //關(guān)鍵字virtual
{
????//使用virtual派生子類的基類稱為虛基類(對virtual派生的子類而言)
????public:
???? ClassDerive1(int a = 0):ClassBaseA(a) {} //虛繼承不影響子類得到虛基類的成員
???? void func() {}
};
class ClassDerive2:virtual public ClassBaseA //ClassDerive1和2都虛繼承ClassBaseA,ClassBaseA稱為虛基類
{
????public:
???? ClassDerive2(int a = 0):ClassBaseA(a) {}
???? void func() {}
};
class ClassDeriveDerive :public ClassDerive1, public ClassDerive2 //子類繼承這些有共同父類的類,這里不需要virtual。子類正常繼承多個父類,多個父類虛繼承同一個類
{
????public:
???? void invoke() {
???????? a = 0; //虛基類的數(shù)據(jù)成員(即虛基類子對象)只有一份
???????? //func(); //如果中間類沒有shadow虛基類的函數(shù),則子類調(diào)用func()直接調(diào)用虛基類::func()且沒有二義性,如果某一個中間類shadow了虛基類的函數(shù),則根據(jù)虛繼承的規(guī)則,距離子類最近的func()生效,如果兩個中間類都shadow了該函數(shù),則兩個func()優(yōu)先級相同產(chǎn)生二義性
???? }
???? ClassDeriveDerive(int a = 0)
???????? :ClassDerive1(a), ClassDerive2(a), ClassBaseA(a) {} //與普通多繼承不同,除了指定直接基類的構(gòu)造器外還需要指定虛基類的構(gòu)造器,虛基類禁止通過中間類構(gòu)造器傳遞信息給基類,子類使用中間類構(gòu)造器初始化時只執(zhí)行中間類自身聲明的部分,該構(gòu)造器調(diào)用的基類構(gòu)造器部分不生效,如果不指定虛基類構(gòu)造器則調(diào)用默認構(gòu)造器無視中間類構(gòu)造器的虛基類部分,實際執(zhí)行順序與構(gòu)造器書寫順序無關(guān)
???? int b;
};
int maininherit9()
{
???? ClassDeriveDerive cdd;
???? ClassDerive1* cd1ptr = &cdd; //子類對象可賦給父類指針(賦值兼容),不會產(chǎn)生新的對象
???? ClassDerive2* cd2ptr = &cdd;
???? //cd1ptr->b; //父類指針無法調(diào)用子類獨有的內(nèi)容,且如果子類shadow了父類的成員,通過父類指針也只能訪問到父類的定義
???? return 0;
}
class ClassFactoryBase //可以使用工廠模式處理虛繼承產(chǎn)生的麻煩
{
????//使用默認的無參構(gòu)造器、析構(gòu)器,即在構(gòu)造階段不初始化任何東西
????protected:
???? int* arr;
???? int size;
????public:
???? void init(int size) //自定義初始化
???? {?
???????? this->size = size;?
???????? this->arr = new int[size];
???? }
???? void release() { delete[]arr; } //自定義釋放堆內(nèi)存
};
class ClassFactoryDerive1 :virtual public ClassFactoryBase
{
????protected:
???? int a;
????public:
???? void init(int size,int a) //shadow父類的初始化
???? {
???????? this->size = size;
???????? this->arr = new int[size];
???????? this->a = a;
???? }
};
class ClassFactoryDerive2 :virtual public ClassFactoryBase
{
????protected:
???? int b;
????public:
???? void init(int size,int b) //shadow父類的初始化
???? {
???????? ClassFactoryBase::init(size); //調(diào)用父類的初始化函數(shù)
???????? this->b = b;
???? }
};
class ClassFactoryDeriveDerive :public ClassFactoryDerive1, public ClassFactoryDerive2
{
????private:
???? int c;
????public:
???? void init(int size, int a,int b,int c) //每個子類都寫自己的初始化函數(shù)和釋放函數(shù)
???? {
???????? this->size = size;
???????? this->arr = new int[size];
???????? this->a = a;
???????? this->b = b;
???????? this->c = c;
???? }
???? void release() { delete[]arr; size = 0; a = 0; b = 0; c = 0; }
???? void func() {}
};
int mainfactory()
{
???? ClassFactoryDeriveDerive c; //創(chuàng)建對象,從祖父類構(gòu)造器開始逐一調(diào)用,但都沒有進行任何初始化操作
???? c.init(5, 1, 2, 3); //單獨初始化
???? c.func(); //正常調(diào)用
???? c.release(); //釋放
???? return 0;
}
class ClassBaseC
{
????private:
???? int basepri;
????public:
???? virtual int& getprimember() //virtual將函數(shù)變?yōu)樘摵瘮?shù),虛函數(shù)用于構(gòu)成多態(tài)
???? {
???? ????return basepri; //函數(shù)用來返回私有成員
???? }
};
class ClassDeriveC1 :public ClassBaseC
{
????private:
???? int derive1pri;
????public:
???? virtual int& getprimember() //重寫override虛函數(shù),要求函數(shù)簽名完全一致??墒÷詖irtual關(guān)鍵字
???? {
???? ????return derive1pri; //返回子類的私有成員
???? }
};
class ClassDeriveC2 :public ClassBaseC
{
????private:
???? int derive2pri;
????public:
???? virtual int& getprimember()
???? {
???? ????return derive2pri;
???? }
};
int mainpoly()
{
ClassBaseC base;
ClassDeriveC1 derive1;
ClassDeriveC2 derive2;
ClassBaseC* baseptr = &base; //父類指針指向父類對象
cout << &baseptr->getprimember() << endl;
cout << &base.getprimember() << endl; //調(diào)用函數(shù)得到父類的數(shù)據(jù)成員basepri,取址&運算符優(yōu)先級低于成員運算符.和間接成員運算符->,結(jié)果將成員的地址輸出
baseptr = &derive1; //父類指針指向子類對象
cout << &baseptr->getprimember() << endl; //通過父類指針調(diào)用到了子類重寫的虛函數(shù),得到了子類獨有的成員derive1pri,構(gòu)成了多態(tài)
cout << &derive1.getprimember() << endl;
baseptr = &derive2; //指向另外一個子類對象,結(jié)果同樣調(diào)用這個子類的成員函數(shù)
cout << &baseptr->getprimember() << endl;
cout << &derive2.getprimember() << endl;
//多態(tài)要求:1父類聲明虛函數(shù),2子類重寫虛函數(shù)(只有函數(shù)簽名完全一樣才構(gòu)成重寫,否則shadow父類的虛函數(shù))3通過父類指針指向子類對象,調(diào)用虛函數(shù)會調(diào)用到對象實際類型的函數(shù)成員
// 通常父類的虛函數(shù)公有,子類的虛函數(shù)訪問限制無所謂不會影響父類指針的函數(shù)調(diào)用,只要父類聲明為虛函數(shù),后代類的該函數(shù)均為虛函數(shù)
// 如果父類的虛函數(shù)返回父類引用或指針,子類重寫的虛函數(shù)允許修改為返回子類引用或指針,這稱為返回類型協(xié)變,協(xié)變只適用于這種情況,不能將void類型重寫為int,也不能將int類型重寫為long
//baseptr = new ClassDeriveC1; //如果用父類指針指向堆內(nèi)存
//delete baseptr; //釋放對象時會調(diào)用父類的析構(gòu)器,導(dǎo)致不完全析構(gòu),正常delete子類指針時會先調(diào)用子類析構(gòu)器再調(diào)用父類析構(gòu)器,delete父類指針跳過了子類的析構(gòu)
return 0;
}
class AbstractBaseClass //抽象基類
{
????public:
???? virtual void connect(const char* address) = 0; //在virtual虛函數(shù)聲明的末尾添加 = 0 使函數(shù)變?yōu)榧兲摵瘮?shù),聲明純虛函數(shù)可以不寫函數(shù)定義,擁有純虛函數(shù)的類不能實例化,類成為抽象基類,只能用于繼承,用于多態(tài),(注意抽象基類和虛基類不同)
???? virtual ~AbstractBaseClass() {} //使用堆內(nèi)存構(gòu)成多態(tài)時,需要將父類的析構(gòu)器virtual,即析構(gòu)器也要多態(tài)。通常抽象基類和使用虛函數(shù)的基類的析構(gòu)器都要虛,即多態(tài)的父類必須虛析構(gòu),父類的析構(gòu)虛了之后,后代的所有析構(gòu)器也都為虛函數(shù),通過父類引用、指針調(diào)用析構(gòu)器時會調(diào)用實際子類的析構(gòu)器,且析構(gòu)器中在用戶定義的代碼(如果有)之后會逐級調(diào)用父類的析構(gòu)器。
};
class ClassDeriveD1 :public AbstractBaseClass
{
????public:
???? virtual void connect(const char* address) //子類需實現(xiàn)父類的所有純虛函數(shù)才能夠?qū)嵗?。否則依然為抽象類
???? {
???? ????cout << "TCP connect" << endl << address << endl;
???? }
};
class ClassDeriveD2 :public AbstractBaseClass
{
????public:
???? virtual void connect(const char* address) //virtual可省略
???? {
???? ????func(address); //可以調(diào)用子類獨有的函數(shù)實現(xiàn)父類的純虛函數(shù)。每個子類都有不同的實現(xiàn)方式,對外統(tǒng)一使用父類屏蔽差異
???? }
???? void func(const char* address) {
???? ????cout << "UDP connect" << endl << address << endl;
???? }
};
class ClassDeriveD3 :public ClassDeriveD2 //抽象類/接口的實現(xiàn)類的子類,會繼承實現(xiàn)類的虛函數(shù)定義,可以按需再次重寫
{
????public:
???? ~ClassDeriveD3() { cout << "ddd" << endl; }
};
int mainabstract()
{
???? AbstractBaseClass* ptr = new ClassDeriveD1;
???? ptr->connect("127.0.0.1");
???? delete ptr;
???? ptr = new ClassDeriveD2;
???? ptr->connect("127.0.0.1");
???? delete ptr;
???? ptr = new ClassDeriveD3;
???? ptr->connect("127.0.0.1");
???? delete ptr; //抽象基類virtual析構(gòu)器后,即便實現(xiàn)類沒有virtual,依然調(diào)用到了實現(xiàn)類的子類的析構(gòu)器
???? //在父類的普通成員函數(shù)中通過this指針調(diào)用虛函數(shù)和在外側(cè)通過父類指針調(diào)用虛函數(shù)一樣,都可以構(gòu)成多態(tài)。
???? //但在父類的構(gòu)造器和析構(gòu)器中使用this指針無法構(gòu)成多態(tài),因為父類的構(gòu)造器先于子類的構(gòu)造器執(zhí)行,此時還沒有初始化子類所以無法調(diào)用,而子類的析構(gòu)器先于父類的析構(gòu)器,在父類析構(gòu)器執(zhí)行時子類獨有的空間已經(jīng)釋放,無法調(diào)用
???? return 0;
}
class ClassVirtual1 //聲明一個帶有虛函數(shù)的類
{
????public:
???? int c;
???? virtual void func() { cout << "base func" << endl; };
};
int mainvtable1()
{
???? cout << sizeof(ClassVirtual1) << endl; //結(jié)果為16,只要類中含有虛函數(shù),在創(chuàng)建對象時會在內(nèi)存塊最前面存放一個指向虛函數(shù)表的指針,本系統(tǒng)指針為8字節(jié),只要有虛函數(shù)表的指針,內(nèi)存空間會變?yōu)?字節(jié)對齊,所以雖然實際使用了12字節(jié),卻占用了16字節(jié)
???? ClassVirtual1 c;
???? __int64 v = *(__int64*)&c; //&c是對象的地址,從該地址開始的前8字節(jié)是指向虛函數(shù)表的指針,指針的值/地址也是數(shù)值類型,將地址解釋成8字節(jié)__int64/longlong類型的值的地址,解引用后得到指針的值/64位int的值,這個值是虛函數(shù)表的地址
???? //虛函數(shù)表中存放有對象的類及祖先中所有虛函數(shù),表中每個元素都是一個函數(shù)指針,即元素是*func,表是*func[]
???? void(**pvtable)() = (void(**)())v; //所以將v的類型轉(zhuǎn)換為指向函數(shù)指針的指針,這個指針變量的值/v的值是指針數(shù)組首元素的地址即&(pfunc),所以用指向函數(shù)指針的指針來接收函數(shù)指針數(shù)組首元素的地址/函數(shù)指針的地址
???? cout << *pvtable << endl; //*pvtable得到首個函數(shù)指針的值,按指針數(shù)組的用法表示為pvtable[0],只有含有虛函數(shù)的類才有虛函數(shù)表,所以首元素函數(shù)指針一定存在
???? (*(*pvtable))(); //通過指針調(diào)用函數(shù),結(jié)果為調(diào)用c.func()
???? return 0;
}
class ClassVirtual2 :public ClassVirtual1
{
????public:
???? void func() { cout << "derive func" << endl; }
???? void func(int a) { cout << "derive func" << endl; }
???? void func2() { cout << "derive func2" << endl; }
};
int mainvtable2()
{
???? ClassVirtual2 c;
???? (*(**(void(***)()) & c))(); //前8字節(jié)是存儲*func[]的指針,這個指針的值是**func類型的值,這個指針是***func類型,或者說這個地址上存儲了虛函數(shù)表的地址作為值,這個值是*func[]類型,這個地址是&(*func[])所以再加一個*,對指向虛函數(shù)表的指針解引用一次得到虛函數(shù)表,解引用兩次得到首元素即函數(shù)指針,對函數(shù)指針解引用加括號調(diào)用
???? //結(jié)果為"derive func"?
???? cout << *(void(***)()) & c << endl; //無論創(chuàng)建多少個CV2對象,虛函數(shù)表指針都指向一張表
???? ClassVirtual1 cc;
???? cout << *(void(***)()) & cc << endl; //父類的虛函數(shù)表地址不同于子類
???? //作為CV1的子類,CV2對父類虛函數(shù)表的副本進行修改和擴充,如果函數(shù)簽名完全相同的虛函數(shù)會覆蓋掉父類的指針,如果是父類沒有的虛函數(shù)會往后擴充。
???? //所以多態(tài)的原理是,將對象的虛函數(shù)表的指針存儲在對象的內(nèi)存空間的首位,無論以哪個父類指針指向該對象,都不影響讀取首位的虛函數(shù)表,而虛函數(shù)表中子類重寫的函數(shù)的地址直接覆蓋了父類的指針的值,所以通過虛函數(shù)表無論如何也調(diào)用不到父類被重寫的原函數(shù),而函數(shù)名相同、函數(shù)簽名不同的經(jīng)過傾軋實際為不同的函數(shù)所以沒有被virtual不會添加到虛函數(shù)表中,這雖然不會重寫原函數(shù),但會shadow,通過子類對象無法正常訪問到,但可以通過虛函數(shù)表訪問,而如果將函數(shù)名相同、函數(shù)簽名不同的函數(shù)也virtual,不會重寫原函數(shù)會往后擴充,但同樣會shadow使得子類無法正常訪問父類的原函數(shù),但可以通過虛函數(shù)表訪問
???? return 0;
}
#include <typeinfo> //提供與typeid有關(guān)的函數(shù)及運算符重載
int mainRTTI()
{
???? //run time type identification運行階段類型識別RTTI
???? ClassVirtual2 c;
???? ClassVirtual1* ptr = &c;
???? cout << typeid(ptr).name() << endl; //typeid關(guān)鍵字,返回包含運算對象信息的type_info對象的引用,.name()返回名字,c++沒有規(guī)定name返回的格式,typeid(ptr)返回指針變量的信息
???? cout << typeid(*ptr).name() << endl; //解引用后,typeid(*ptr)返回對象的實際類型信息,typeid通常用于多態(tài)中顯示對象實際信息
???? if (typeid(*ptr) == typeid(ClassVirtual2)) //判斷*ptr對象的類型是否為ClassVirtual2類(子類不算),typeinfo頭文件重載了賦值運算符== !=
???? {
???? ????cout << "*ptr is an instance of ClassVirtual2" << endl;
???? }
???? ClassVirtual2* realptr = static_cast<ClassVirtual2*>(ptr); //子類地址賦值給父類指針不需要顯式轉(zhuǎn)換,父類指針/地址轉(zhuǎn)換為子類指針時需要顯式轉(zhuǎn)換。如果使用static_cast<類型>(要轉(zhuǎn)換的變量),不會判斷該對象的實際類型是否為要轉(zhuǎn)換的類型或子類,即使用static_cast可以將指向父類對象的父類指針轉(zhuǎn)換為子類指針而不報錯,不安全,這個轉(zhuǎn)換是編譯階段執(zhí)行的,編譯器不會查看上下文判斷該對象的實際類型。但如果將指針轉(zhuǎn)換為沒有繼承關(guān)系的其他指針會報錯
???? //報錯:AbstractBaseClass* ptr2 = static_cast<AbstractBaseClass*>(ptr); 編譯器判斷兩種類型無繼承關(guān)系
???? //static_cast轉(zhuǎn)換需要源類型可以隱式轉(zhuǎn)換為目標類型或者目標類型可以隱式轉(zhuǎn)換為源類型,即使派生類沒有定義使用基類的轉(zhuǎn)換構(gòu)造器但因為派生類可以隱式轉(zhuǎn)換為基類所以static_cast<派生類*>基類* 能通過編譯。static_cast不止用于類,任何可以隱式轉(zhuǎn)換的兩種類型都可以使用該轉(zhuǎn)換運算符
???? ClassVirtual2* realptr2 = dynamic_cast<ClassVirtual2*>(ptr); //dynamic_cast轉(zhuǎn)換運算符只用于多態(tài)的指針/引用轉(zhuǎn)換。動態(tài)轉(zhuǎn)換,對于轉(zhuǎn)換指針在運行時判斷指針指向?qū)ο蟮膶嶋H類型,如果對象是目標類型的實例或后代類型的實例則成功轉(zhuǎn)換,除此以外均返回null,對于轉(zhuǎn)換引用如果轉(zhuǎn)換不成功會引發(fā)bad_cast異常
???? if (realptr2 != NULL) //判斷是否轉(zhuǎn)換成功,安全,可以和上一句合并為if( ClassVirtual2* realptr2 = dynamic_cast<ClassVirtual2*>(ptr) ){}
???? {
???? ????cout << "*ptr is an instance of ClassVirtual2" << endl;
???? }
???? //dynamic_cast會檢測基類指針是否能安全轉(zhuǎn)換為派生類指針而static_cast只檢測兩種類型是否相關(guān)
???? AbstractBaseClass* ptr2 = reinterpret_cast<AbstractBaseClass*>(ptr); //reinterpret_cast將變量重新解釋為目標類型,完全不檢驗類型。完全不安全。不能刪除const特性,不能將源類型轉(zhuǎn)換為size更小的類型,不能將函數(shù)指針和數(shù)據(jù)指針相互轉(zhuǎn)換。
???? //typeid和dynamic_cast通常只用于多態(tài),假設(shè)有A繼承B繼承C,A的對象賦給C的指針,如果將C指針轉(zhuǎn)換為B指針,無法通過typeid(*指針)==typeid(B)得到是否能轉(zhuǎn)換成功,使用dynamic_cast<B*>(C指針)判斷是否NULL即可完成中間類的轉(zhuǎn)換及確認
???? //通常,抽象類型/接口夾在高層業(yè)務(wù)邏輯和底層實現(xiàn)之間(高層和底層都依賴抽象類型),高層業(yè)務(wù)邏輯使用抽象類通過多態(tài)完成業(yè)務(wù)邏輯,即使用抽象的稱為高層。實現(xiàn)抽象的稱為底層實現(xiàn)。通常高層業(yè)務(wù)邏輯一經(jīng)敲定很難更改/基本不變,需要擴展、修改業(yè)務(wù)時通過調(diào)整、追加底層實現(xiàn)來完成。
???? return 0;
}
class Port
{
???? char* brand;
???? char style[20];
???? int bottles;
????public:
???? Port(const char* br = "none", const char* st = "none", int b = 0) :bottles(b)?
???? {
???????? brand = new char[strlen(br) + 1];
???????? strcpy(brand, br);
???????? strncpy(style, st, 19);
???????? style[19] = 0;
???? }
???? Port(const Port& p) :bottles(p.bottles)
???? {
???????? brand = new char[strlen(p.brand) + 1];
???????? strcpy(brand, p.brand);
???????? strcpy(style, p.style);
???? }
???? virtual ~Port() { delete[]brand; } //釋放動態(tài)內(nèi)存
???? Port& operator=(const Port& p)
???? {
???????? if (&p == this) //賦值運算符重載必須先排除調(diào)用對象給自己賦值的特例
???????? ????return *this;
???????? delete[]brand;
???????? brand = new char[strlen(p.brand) + 1];
???????? strcpy(brand, p.brand);
???????? strcpy(style, p.style);
???????? bottles = p.bottles;
???? }
???? Port&operator+=(int b)
???? {
???????? bottles += b;
???????? return *this;
???? }
???? Port&operator-=(int b)
???? {
???????? bottles -= b;
???????? return *this;
???? }
???? int BottleCount()const { return bottles; }
???? virtual void show()const
???? {
???? ????cout << "Brand:" << brand << "\nKind:" << style << "\nBottles:" << bottles << endl;
???? }
???? friend ostream& operator<<(ostream& os, const Port& p)
???? {
???????? os << p.brand << "," << p.style << ',' << p.bottles;
???????? return os;
???? }
};
class VintagePort :public Port
{
???? char* nickname;
???? int year;
????public:
???? VintagePort() :year(0) //隱式調(diào)用Port()構(gòu)造器,執(zhí)行基類的動態(tài)分配
???? {
???????? nickname = new char[1]; //派生類的動態(tài)分配
???????? nickname[0] = 0;
???? }
???? VintagePort(const char* br, const char* st, int b, const char* nn, int y) :Port(br, st, b), year(y)
???? {
???????? nickname = new char[strlen(nn) + 1];
???????? strcpy(nickname, nn);
???? }
???? VintagePort(const VintagePort& vp) :Port(vp), year(vp.year) //使用Port(const Port&)拷貝構(gòu)造器傳參VintagePort對象,基類引用可以指向派生類對象,對當前對象的Port部分進行初始化
???? {
???????? nickname = new char[strlen(vp.nickname) + 1];
???????? strcpy(nickname, vp.nickname);
???? }
???? ~VintagePort() { delete[]nickname; } //基類虛析構(gòu),派生類需要手動釋放派生類中聲明、分配的動態(tài)內(nèi)存,用戶定義代碼之后會自動調(diào)用基類的析構(gòu)器釋放基類的動態(tài)內(nèi)存
???? VintagePort& operator=(const VintagePort& vp) //因為派生類中也使用了動態(tài)內(nèi)存,所以需要自定義賦值運算符重載
???? {
???????? if (&vp == this)
???????? ????return *this;
???????? Port::operator=(vp); //顯式調(diào)用基類的賦值運算符,對調(diào)用對象的Port部分進行賦值(派生類的隱式的賦值運算符重載也會調(diào)用基類的賦值運算符來設(shè)置基類部分成員)
???????? delete[]nickname;
???????? nickname = new char[strlen(vp.nickname) + 1];
???????? strcpy(nickname, vp.nickname);
???????? year = vp.year;
???????? return *this;
???? }
???? void show()const
???? {
???????? Port::show(); //調(diào)用基類的show
???????? cout << "Nickname:" << nickname << "\nyear:" << year << endl;
???? }
???? friend ostream& operator<<(ostream& os, const VintagePort& vp)
???? {
???????? os << (const Port&)vp << ',' << vp.nickname << ',' << vp.year; //友元函數(shù)不屬于類不能通過作用域解析運算符調(diào)用,通過將派生類引用類型轉(zhuǎn)換為基類引用類型來匹配使用基類引用類型的插入運算符
???????? return os;
???? }
};
class Person
{
???? string first_;
???? string last_;
????public:
???? Person(const char* first = "F", const char* last = "L") :first_(first), last_(last) {}
???? virtual ~Person() {}
???? virtual void Show()
???? {
???? ????cout << first_ << ' ' << last_ << endl;
???? }
};
class Gunslinger:virtual public Person
{
???? double draw_;
???? int ke_;
????public:
???? Gunslinger(const char* first = "G", const char* last = "L", double draw = 1.0, int ke = 2) :Person(first, last), draw_(draw), ke_(ke) {}
???? void gShow()
???? {
???? ????cout << "time:" << draw_ << " ke:" << ke_ << endl;
???? }
???? virtual void Show()
???? {
???????? Person::Show();
???????? gShow(); //模塊化,將派生類的新內(nèi)容單獨寫一個函數(shù)在這里調(diào)用,以這個類為基類派生的類可以通過gShow()只顯示這個類獨有的內(nèi)容避免兩次調(diào)用Person::Show()
???? }
???? double Draw() { return draw_; }
};
class PokerPlayer :virtual public Person
{
????public:
???? PokerPlayer(const char* first = "P", const char* last = "P") :Person(first, last) {}
???? int Draw()
???? {
???? ????return rand() % 52 + 1;
???? }
};
class BadDude :public Gunslinger, public PokerPlayer
{
???? void Draw() {} //私有禁用,避免二義性
????public:
???? BadDude(const char* first = "B", const char* last = "D", double draw = 1.0, int ke = 2) :Person(first, last), Gunslinger(first, last, draw, ke), PokerPlayer(first, last) {}
???? double Gdraw() { return Gunslinger::Draw(); }
???? int Cdraw() { return PokerPlayer::Draw(); }
???? void Show()
???? {
???? PokerPlayer::Show(); //PokerPlayer沒有重新定義show,否則應(yīng)調(diào)用Person::show、PokerPlayer::pShow和Gunslinger::gShow來避免重復(fù)展示姓名
???? Gunslinger::gShow();
???? }
};