您好,登錄后才能下訂單哦!
1.概括
在CPlusPlus多繼承編程中時常遇到這樣一個問題--若子類實現多個基類或接口繼承,多基類或接口中存在成員名相同,在客戶與實現類之間的通信時編譯器報錯“不能這樣使用,會產生二義性”由于這個問題的解決方法很多。比如說,可以把相同的成員名給改過來。但是,從專業的角度,可能虛擬繼承會解決這個問題。那接下來我看看c++是怎么避免這種問題的。
2.概念
當在多條繼承路徑上有一個公共的基類,在這些路徑中的某幾條匯合處,這個公共的基類就會產生多個實例(或多個副本),若只想保存這個基類的一個實例,可以將這個公共基類說明為虛基類。 class 派生類名:virtual 繼承方式 基類名
virtual是關鍵字,聲明該基類為派生類的虛基類。
例如:
class 派生類: virtual 基類1,virtual 基類2,...,virtual 基類n { ...//派生類成員聲明 };
在多繼承情況下,虛基類關鍵字的作用范圍和繼承方式關鍵字相同,只對緊跟其后的基類起作用。 聲明了虛基類之后,虛基類在進一步派生過程中始終和派生類一起,維護同一個基類子對象的拷貝。C++使用虛擬繼承(Virtual Inheritance),解決從不同途徑繼承來的同名的數據成員在內存中有不同的拷貝造成數據不一致問題,將共同基類設置為虛基類。這時從不同的路徑繼承過來的同名數據成員在內存中就只有一個拷貝,同一個函數名也只有一個映射。這樣帶來的有點是解決了二義性問題,也節省了內存,避免了數據不一致的問題。
3.用例
二義性:
#include <iostream> using namespace std; //Base class Base { public: Base(){cout << "Base called..."<< endl;} void print(){cout << "Base print..." <<endl;} private: }; //Sub class Sub //定義一個類 Sub { public: Sub(){cout << "Sub called..." << endl;} void print(){cout << "Sub print..." << endl;} private: }; //Child class Child : public Base , public Sub //定義一個類Child 分別繼承自 Base ,Sub { public: Child(){cout << "Child called..." << endl;} private: }; int main(int argc, char* argv[]) { Child c; //不能這樣使用,會產生二意性,VC下error C2385 //c.print(); //只能這樣使用 c.Base::print(); c.Sub::print(); system("pause"); return 0; }
多重繼承:
//說明:C++虛擬繼承學習演示 //環境:VS2005 //blog:pppboy.blog.163.com //---------------------------------------------------- #include "stdafx.h" #include <iostream> using namespace std; int gFlag = 0; class Base { public: Base(){cout << "Base called : " << gFlag++ << endl;} void print(){cout << "Base print" <<endl;} }; class Mid1 : public Base { public: Mid1(){cout << "Mid1 called" << endl;} private: }; class Mid2 : public Base { public: Mid2(){cout << "Mid2 called" << endl;} }; class Child:public Mid1, public Mid2 { public: Child(){cout << "Child called" << endl;} }; int main(int argc, char* argv[]) { Child d; //不能這樣使用,會產生二意性 //d.print(); //只能這樣使用 d.Mid1::print(); d.Mid2::print(); system("pause"); return 0; }
output:
Base called : 0
Mid1 called
Base called : 1
Mid2 called
Child called
Base print
Base print
虛擬繼承:
#include "stdafx.h" #include <iostream> using namespace std; int gFlag = 0; class Base { public: Base(){cout << "Base called : " << gFlag++ << endl;} void print(){cout << "Base print" <<endl;} }; class Mid1 : virtual public Base { public: Mid1(){cout << "Mid1 called" << endl;} private: }; class Mid2 : virtual public Base { public: Mid2(){cout << "Mid2 called" << endl;} }; class Child:public Mid1, public Mid2 { public: Child(){cout << "Child called" << endl;} }; int main(int argc, char* argv[]) { Child d; //這里可以這樣使用 d.print(); //也可以這樣使用 d.Mid1::print(); d.Mid2::print(); system("pause"); return 0; }
4.總結
在多繼承情況下,虛基類關鍵字的作用范圍和繼承方式關鍵字相同,只對緊跟其后的基類起作用。聲明了虛基類之后,虛基類在進一步派生過程中始終和派生類一起,維護同一個基類子對象的拷貝。觀察類構造函數的構造順序,拷貝也只有一份。
5.擴展
windows編程的程序員們在進行COM編程的時候會遇到這樣的一個問題------繼承接口IUnknown這一塊使用非虛擬繼承。這是為什么呢?如果有這樣的疑問是很正常。之所以這樣是由于會導致與COM不兼容的vtbl。比如:
struct IX : public IUnknown
{
//....
};
struct IY : public IUnknow
{
//....
};
客戶程序實現:
... if(iid == IID_IUnknown) { //the client wants the IUnknown interface. *ppv = static_cast<IX*>(this); } else if(iid == IID_IX){ //the client wants the IX interface. *ppv = static_cast<IX*>(this); } else if(iid == IID_IY) { *ppv = static_cast<IY*>(this); } ...
可見,程序中他們是通過類型轉換的。不然,IX和IY的vtbl中的頭三個函數指向的將不是IUnknown的三個成員函數。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。