在多重繼承中,需要解決的主要問題是標(biāo)識符不唯一,即二義性問題。比如,當(dāng)在派生類繼承的多個(gè)基類中有同名成員時(shí),派生類中就會出現(xiàn)標(biāo)識符不唯一的情況。
在多重繼承中,派生類由多個(gè)基類派生時(shí),基類之間用逗號隔開,且每個(gè)基類前都必須指明繼承方式,否則默認(rèn)是私有繼承。可以通過以下3種方法解決二義性問題。
使用運(yùn)算符"::";
使用同名覆蓋的原則;
使用虛基類;
1.使用域運(yùn)算符
如果派生類的基類之間沒有繼承關(guān)系,同時(shí)又沒有共同的基類,則在引用同名成員時(shí),可以在成員名前面加上類名和域運(yùn)算符來區(qū)別來自不同基類的成員。
2.使用同名覆蓋的原則
在派生類中重新定義與基類中同名的成員(如果是成員函數(shù),則參數(shù)表也要相同,否則是重載)以屏蔽掉基類中的同名成員,在引用這些同名成員時(shí),引用的就是派生類中的成員。
#include
using namespace std;
class Base
{
public:
int x;
void show()
{
cout << "Base,x= " << x << endl;
}
};
class Derived :public Base
{
public:
int x;
void show()
{
cout << "Derived , x= " << x << endl;
}
};
int main()
{
Derived ob;
ob.x = 5;
ob.show();
ob.Base::show();
ob.Base::x = 12;
ob.Base::show();
system("pause");
return 0;
}
3.虛基類
在多重繼承中,要引用派生類的成員時(shí),先是在派生類自身的作用域內(nèi)尋找,如果找不到再到基類中尋找。這時(shí),如果這些基類有一個(gè)共同的基類,派生類訪問這個(gè)共同基類的成員時(shí),就有可能由于同名成員的問題而發(fā)生二義性,此時(shí)就需要虛基類來解決。
#include
using namespace std;
class Base
{
public:
Base()
{
x = 1;
}
protected:
int x;
};
class Base1 :public Base
{
public:
Base1()
{
cout << "Base1,x= " << x << endl;
}
};
class Base2 :public Base
{
public:
Base2()
{
cout << "Base2,x= " << x << endl;
}
};
class Derived :public Base1, public Base2
{
public:
Derived()
{
cout << "Derived,x= " << x << endl;
}
};
int main()
{
Derived obj;
system("pause");
return 0;
}
上邊的代碼表面上看類Base1和類Base2是從同一個(gè)基類Base派生出來的,但是其對應(yīng)的卻是基類Base的兩個(gè)不同的復(fù)制。因此,當(dāng)派生類Derived要訪問變量x時(shí)不知從哪條路徑去尋找,從而引發(fā)二義性問題。
上述代碼對應(yīng)的類層次結(jié)構(gòu)如圖1所示,屬于非虛基類的類層次結(jié)構(gòu)。要解決該問題,需要引入虛基類,其具體的做法是將公共基類聲明為虛基類,這樣這個(gè)公共基類就只有一個(gè)拷貝,從而不會出現(xiàn)二義性問題。虛基類的類層次結(jié)構(gòu)如圖2所示。
圖 1
圖 2
3.1虛基類
虛基類的聲明是在派生類的聲明過程中進(jìn)行的,其聲明的一般形式為:
class<派生類名>:virtual <派生方式><基類名>
這種派生方式叫做虛擬繼承,虛基類關(guān)鍵字的作用范圍和派生方式與一般派生類的聲明一樣,只對緊跟其后的基類起作用。聲明了虛基類以后,虛基類的成員在進(jìn)一步的派生過程中和派生類一起維護(hù)同一個(gè)內(nèi)存拷貝。
#include
using namespace std;
class Base
{
public:
Base()
{
x = 1;
}
protected:
int x;
};
class Base1 :virtual public Base
{
public:
Base1()
{
cout << "Base1,x= " << x << endl;
}
};
class Base2 :virtual public Base
{
public:
Base2()
{
cout << "Base2,x= " << x << endl;
}
};
class Derived :public Base1, public Base2
{
public:
Derived()
{
cout << "Derived,x= " << x << endl;
}
};
int main()
{
Derived obj;
system("pause");
return 0;
}
在上述代碼中,由于把公共基類Base聲明為類Base1和Base2的虛基類,所以由類Base1和類Base2派生的類Derived只有一個(gè)基類Base,消除了二義性。
3.2虛基類構(gòu)造函數(shù)和初始化
虛基類的初始化與一般的多繼承的初始化在語法上是一樣的,但是構(gòu)造函數(shù)的執(zhí)行順序不同。主要在以下方面:
虛基類的構(gòu)造函數(shù)的執(zhí)行在非虛基類的構(gòu)造函數(shù)之前;
若同一層次中包含多個(gè)虛基類,這些虛基類的構(gòu)造函數(shù)按照他們被聲明的先后順序執(zhí)行;
若虛基類由非虛基類派生而來,則仍然先執(zhí)行基類的構(gòu)造函數(shù),再執(zhí)行派生類的構(gòu)造函數(shù)。
#include
using namespace std;
class Base
{
public:
Base(int x1)
{
x = x1;
cout << "Base,x= " << x << endl;
}
protected:
int x;
};
class Base1 :virtual public Base
{
int y;
public:
Base1(int x1, int y1) :Base(x1)
{
y = y1;
cout << "Base1 ,y=" << y << endl;
}
};
class Base2 :virtual public Base
{
int z;
public:
Base2(int x1, int z1) :Base(x1)
{
z = z1;
cout << "Base2,z= " << z << endl;
}
};
class Derived :public Base1, public Base2
{
int xyz;
public:
Derived(int x1, int y1, int z1, int xyz1) :Base(x1), Base1(x1,y1), Base2(x1,z1)
{
xyz = xyz1;
cout << "Derived,xyz = " << xyz << endl;
}
};
int main()
{
Derived obj(1, 2, 3, 4);
system("pause");
return 0;
}
上邊的代碼中,虛基類Base的構(gòu)造函數(shù)只執(zhí)行了一次,這是因?yàn)楫?dāng)派生類Derived調(diào)用了虛基類Base的構(gòu)造函數(shù)之后,類Base1和Base2對虛基類Base構(gòu)造函數(shù)的調(diào)用就被忽略,這是初始化虛基類和初始化非虛基類的不同。
在使用虛基類時(shí)要注意:
虛基類的關(guān)鍵字virtual與派生方式的關(guān)鍵字public,private,protected的書寫位置無關(guān)緊要,可以先寫虛基類的關(guān)鍵字,也可以先寫派生 方式的關(guān)鍵字;
一個(gè)基類在作為某些類的虛基類的同時(shí)可以作為另一些類的非虛基類;
虛基類構(gòu)造函數(shù)的參數(shù)必須由最新派生出來的類負(fù)責(zé)初始化,即使不是直接繼承也應(yīng)如此。
-
C++
+關(guān)注
關(guān)注
21文章
2100瀏覽量
73453
原文標(biāo)題:C++多繼承的二義性問題
文章出處:【微信號:gh_bee81f890fc1,微信公眾號:面包板社區(qū)】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。
發(fā)布評論請先 登錄
相關(guān)推薦
評論