在多重繼承中,需要解決的主要問題是標識符不唯一,即二義性問題。比如,當在派生類繼承的多個基類中有同名成員時,派生類中就會出現標識符不唯一的情況。
在多重繼承中,派生類由多個基類派生時,基類之間用逗號隔開,且每個基類前都必須指明繼承方式,否則默認是私有繼承。可以通過以下3種方法解決二義性問題。
使用運算符"::";
使用同名覆蓋的原則;
使用虛基類;
1.使用域運算符
如果派生類的基類之間沒有繼承關系,同時又沒有共同的基類,則在引用同名成員時,可以在成員名前面加上類名和域運算符來區別來自不同基類的成員。
2.使用同名覆蓋的原則
在派生類中重新定義與基類中同名的成員(如果是成員函數,則參數表也要相同,否則是重載)以屏蔽掉基類中的同名成員,在引用這些同名成員時,引用的就是派生類中的成員。
#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.虛基類
在多重繼承中,要引用派生類的成員時,先是在派生類自身的作用域內尋找,如果找不到再到基類中尋找。這時,如果這些基類有一個共同的基類,派生類訪問這個共同基類的成員時,就有可能由于同名成員的問題而發生二義性,此時就需要虛基類來解決。
#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是從同一個基類Base派生出來的,但是其對應的卻是基類Base的兩個不同的復制。因此,當派生類Derived要訪問變量x時不知從哪條路徑去尋找,從而引發二義性問題。
上述代碼對應的類層次結構如圖1所示,屬于非虛基類的類層次結構。要解決該問題,需要引入虛基類,其具體的做法是將公共基類聲明為虛基類,這樣這個公共基類就只有一個拷貝,從而不會出現二義性問題。虛基類的類層次結構如圖2所示。
圖 1
圖 2
3.1虛基類
虛基類的聲明是在派生類的聲明過程中進行的,其聲明的一般形式為:
class<派生類名>:virtual <派生方式><基類名>
這種派生方式叫做虛擬繼承,虛基類關鍵字的作用范圍和派生方式與一般派生類的聲明一樣,只對緊跟其后的基類起作用。聲明了虛基類以后,虛基類的成員在進一步的派生過程中和派生類一起維護同一個內存拷貝。
#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只有一個基類Base,消除了二義性。
3.2虛基類構造函數和初始化
虛基類的初始化與一般的多繼承的初始化在語法上是一樣的,但是構造函數的執行順序不同。主要在以下方面:
虛基類的構造函數的執行在非虛基類的構造函數之前;
若同一層次中包含多個虛基類,這些虛基類的構造函數按照他們被聲明的先后順序執行;
若虛基類由非虛基類派生而來,則仍然先執行基類的構造函數,再執行派生類的構造函數。
#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的構造函數只執行了一次,這是因為當派生類Derived調用了虛基類Base的構造函數之后,類Base1和Base2對虛基類Base構造函數的調用就被忽略,這是初始化虛基類和初始化非虛基類的不同。
在使用虛基類時要注意:
虛基類的關鍵字virtual與派生方式的關鍵字public,private,protected的書寫位置無關緊要,可以先寫虛基類的關鍵字,也可以先寫派生 方式的關鍵字;
一個基類在作為某些類的虛基類的同時可以作為另一些類的非虛基類;
虛基類構造函數的參數必須由最新派生出來的類負責初始化,即使不是直接繼承也應如此。
-
C++
+關注
關注
22文章
2108瀏覽量
73623
原文標題:C++多繼承的二義性問題
文章出處:【微信號:gh_bee81f890fc1,微信公眾號:面包板社區】歡迎添加關注!文章轉載請注明出處。
發布評論請先 登錄
相關推薦
評論