新聞中心
在繼承中經(jīng)常會遇到這種情況:有一個超類A,子類B1,B2繼承了A,而類D又繼承了父類B1,B2。在這種情況下如果按照我們以前的正常的菱形繼承的話會有一個問題是子類C會繼承兩次超類A中的成員,當(dāng)在C中想訪問繼承來自B1,B2中A的元素會出現(xiàn)兩個問題:
合山ssl適用于網(wǎng)站、小程序/APP、API接口等需要進行數(shù)據(jù)傳輸應(yīng)用場景,ssl證書未來市場廣闊!成為創(chuàng)新互聯(lián)的ssl證書銷售渠道,可以享受市場價格4-6折優(yōu)惠!如果有意向歡迎電話聯(lián)系或者加微信:13518219792(備注:SSL證書合作)期待與您的合作!問題一、數(shù)據(jù)的冗余
問題二、訪問的二意性
出現(xiàn)了這種問題那么我們該如何解決呢?
C++中為了解決這個問題引入了虛擬菱形繼承,那么虛擬菱形繼承是怎么解決的呢?
首先給大家先畫兩個圖比較下菱形繼承和虛擬菱形繼承在繼承時在內(nèi)存中的成員分布情況:
一、 菱形繼承:
沒有繼承以前的超類A和父類B1,B2;
繼承超類A以后的B1,B2;
子類D繼承B1,B2以后的內(nèi)存分布情況
通過圖我們可以看出菱形繼承存在很多的數(shù)據(jù)冗余,如超類A的成員ia,ca都有兩份,訪問時也會出先二義性的錯誤。
二、虛擬菱形繼承
沒有繼承以前的超類A和父類B1,B2;
虛擬繼承超類A以后的B1,B2;
虛擬繼承B1,B2后的D;
看完分布圖以后,我們看下代碼和D在內(nèi)存中的分布
#includeusing namespace std; class A { public: int ia; char ca; public: A() :ia(0) , ca('A') {} virtual void f() { cout << "A::f()" << endl; } virtual void Bf() { cout << "A::Af()" << endl; } }; class B1:virtual public A { public: int ib1; char cb1; public: B1() :ib1(1) , cb1('1') {} virtual void f() { cout << "B1::f()" << endl; } virtual void f1() { cout << "B1::f1()" << endl; } virtual void B1f() { cout << "B1::B1f()" << endl; } }; class B2:virtual public A { public: int ib2; char cb2; public: B2() :ib2(2) , cb2('2') {} virtual void f() { cout << "B2::f()" << endl; } virtual void f2() { cout << "B2::f2()" << endl; } virtual void B2f() { cout << "B2::B2f()" << endl; } }; class D :public B1,public B2 { public: int id; char cd; public: D() :id(3) , cd('D') {} virtual void f() { cout << "D::f()" << endl; } virtual void f1() { cout << "D::f1()" << endl; } virtual void f2() { cout << "D::f2()" << endl; } virtual void Df() { cout << "D::Df()" << endl; } }; typedef void(*Fun)(); void PrintVTable(int* VTable) { cout << " 虛表地址>" << VTable << endl; for (int i = 0; VTable[i] != 0; ++i) { printf(" 第%d個虛函數(shù)地址 :0X%x,->", i, VTable[i]); Fun f = (Fun)VTable[i]; f(); } } void test() { A a; B1 b1; B2 b2; D d1; cout << "sizeof(A)::" << sizeof(a) << endl; cout << "sizeof(B1)::" << sizeof(b1) << endl; cout << "sizeof(B2)::" << sizeof(b2) << endl; cout << "sizeof(D)::" << sizeof(d1) << endl; int* VTable = (int*)(*(int*)&d1); PrintVTable(VTable); cout << " 虛基表指針->: " << (int*)((int*)&d1 + 1) << endl; cout << " B1::ib1 = " << *(int*)((int*)&d1 + 2) << endl; cout << " B1::cb1 =" << (char)*((int*)&d1 + 3) << endl; VTable = (int*)*((int*)&d1 + 4); PrintVTable(VTable); cout << " 虛基表指針->:" << (int*)((int*)&d1 + 5) << endl; cout << " B2::ib2 =" << *(int*)((int*)&d1 + 6) << endl; cout << " B2::cb2 =" << (char)*((int*)&d1 + 7) << endl; cout << " D::ID =" << *((int*)&d1 + 8) << endl; cout << " D::cd =" << (char)*((int*)&d1 + 9) << endl; cout << " 虛基表的偏移地址->:"<<(int*)((int*)&d1 + 10) << endl; VTable = (int*)*((int*)&d1 + 11); PrintVTable(VTable); cout << " A::ia =" << *(int*)((int*)&d1 + 12) << endl; cout << " A::ca =" << (char)*((int*)&d1 + 13) << endl; } int main() { test(); system("pause"); return 0; }
一、父類b1的內(nèi)存分布情況
二、父類b2的內(nèi)存分布情況
三、子類d1的內(nèi)存分布情況
這些都跟前面畫圖分析的一樣。我們再看一下每個類的大小:
將菱形繼承與虛擬菱形繼承做比較:
按照正常情況下:在菱形繼承與虛擬菱形繼承時,超類大小一樣,但從父類開始大小發(fā)生區(qū)別,父類多了12個字節(jié),子類多了8個字節(jié)。
由于要想消除二義性與冗余性,就得將B1、B2中的A部分變?yōu)橐环?,那只能將B1、B2中A中共同的部分變?yōu)橹羔樦赶駼ase部分。為什么會這樣呢?
一、對于父類B1、B2來說因為多產(chǎn)生了三個指針,前圖中沒畫出來,可以參照子類D的圖,通過虛擬繼承,多增加了一個虛基表指針,一個虛基表的偏移地址,另外還繼承了A的虛表,作用是指向一個地址,地址中保存著父類增加的指針的地址與超類的地址偏移值,通過地址與偏移值相加,找到超類成員部分,并且兩個父類指針都指向的是同一塊空間。所以多了12個字節(jié)。
二、同理,對于子類D來說在同時多了這些東西的同時減去重復(fù)繼承的超類的成員最后就只是多了8字節(jié)
通過這種處理子類中父類與超類公共部分都是同一塊存儲空間,就可以解決菱形繼承的二義性與數(shù)據(jù)冗余問題了。
創(chuàng)新互聯(lián)www.cdcxhl.cn,專業(yè)提供香港、美國云服務(wù)器,動態(tài)BGP最優(yōu)骨干路由自動選擇,持續(xù)穩(wěn)定高效的網(wǎng)絡(luò)助力業(yè)務(wù)部署。公司持有工信部辦法的idc、isp許可證, 機房獨有T級流量清洗系統(tǒng)配攻擊溯源,準(zhǔn)確進行流量調(diào)度,確保服務(wù)器高可用性。佳節(jié)活動現(xiàn)已開啟,新人活動云服務(wù)器買多久送多久。
本文標(biāo)題:【C++】菱形繼承與虛擬菱形繼承的對比分析-創(chuàng)新互聯(lián)
網(wǎng)頁URL:http://www.dlmjj.cn/article/ccpdsi.html