多繼承可以看作是單繼承的擴(kuò)展。所謂多繼承是指派生類具有多個(gè)基類,派生類與每個(gè)基類之間的關(guān)系仍可看作是一個(gè)單繼承。
多繼承下派生類的定義格式如下:
class <派生類名>:<繼承方式1><基類名1>,<繼承方式2><基類名2>,…
{
<派生類類體>
};
其中,<繼承方式1>,<繼承方式2>,…是三種繼承方式:public、private、protected之一。例如:
class A
{
…
};
class B
{
…
};
class C : public A, public B
{
…
};
其中,派生類C具有兩個(gè)基類(類A和類B),因此,類C是多繼承的。按照繼承的規(guī)定,派生類C的成員包含了基類A, B中成員以及該類本身的成員。
多繼承的構(gòu)造函數(shù)
在多繼承的情況下,派生類的構(gòu)造函數(shù)格式如下:
<派生類名>(<總參數(shù)表>):<基類名1>(<參數(shù)表1>),<基類名2>(<參數(shù)表2>),…
<子對(duì)象名>(<參數(shù)表n+1>),…
{
<派生類構(gòu)造函數(shù)體>
}
其中,<總參數(shù)表>中各個(gè)參數(shù)包含了其后的各個(gè)分參數(shù)表。
多繼承下派生類的構(gòu)造函數(shù)與單繼承下派生類構(gòu)造函數(shù)相似,它必須同時(shí)負(fù)責(zé)該派生類所有基類構(gòu)造函數(shù)的調(diào)用。同時(shí),派生類的參數(shù)個(gè)數(shù)必須包含完成所有基類初始化所需的參數(shù)個(gè)數(shù)。
派生類構(gòu)造函數(shù)執(zhí)行順序是先執(zhí)行所屬基類的構(gòu)造函數(shù),再執(zhí)行派生類本身構(gòu)造函數(shù),處于同一層次的各基類構(gòu)造函數(shù)的執(zhí)行順序取決于定義派生類時(shí)所指定的各基類順序,與派生類構(gòu)造函數(shù)中所定義的成員初始化列表的各項(xiàng)順序無(wú)關(guān)。也就是說(shuō),執(zhí)行基類構(gòu)造函數(shù)的順序取決于定義派生類時(shí)基類的順序。可見(jiàn),派生類構(gòu)造函數(shù)的成員初始化列表中各項(xiàng)順序可以任意地排列。
下面通過(guò)一個(gè)例子來(lái)說(shuō)明派生類構(gòu)造函數(shù)的構(gòu)成及其執(zhí)行順序。
#include <iostream.h>
class B1
{
public:
B1(int i)
{
b1 = i;
cout《"構(gòu)造函數(shù) B1."《i《 endl;
}
void print()
{
cout《"B1.print()"《b1《endl;
}
private:
int b1;
};
class B2
{
public:
B2(int i)
{
b2 = i;
cout《"構(gòu)造函數(shù) B2."《i《 endl;
}
void print()
{
cout《"B2.print()"《b2《endl;
}
private:
int b2;
};
class B3
{
public:
B3(int i)
{
b3 = i;
cout《"構(gòu)造函數(shù) B3."《i《endl;
}
int getb3()
{
return b3;
}
private:
int b3;
};
class A : public B2, public B1
{
public:
A(int i, int j, int k, int l):B1(i), B2(j), bb(k)
{
a = l;
cout《"構(gòu)造函數(shù) A."《a《endl;
}
void print()
{
B1::print();
B2::print();
cout《"A.print()"《a《","《bb.getb3()《endl;
}
private:
int a;
B3 bb;
};
void main()
{
A aa(1, 2, 3, 4);
aa.print();
}
該程序的輸出結(jié)果為:
構(gòu)造函數(shù) B2.2
構(gòu)造函數(shù) B1.1
構(gòu)造函數(shù) B3.3
構(gòu)造函數(shù) A.4
B1.print()。1
B2.print()2
A.print()4, 3
在該程序中,作用域運(yùn)算符::用于解決作用域沖突的問(wèn)題。在派生類A中的print()函數(shù)的定義中,使用了B1::print;和B2::print();語(yǔ)句分別指明調(diào)用哪一個(gè)類中的print()函數(shù),這種用法應(yīng)該學(xué)會(huì)。
二義性問(wèn)題
一般說(shuō)來(lái),在派生類中對(duì)基類成員的訪問(wèn)應(yīng)該是唯一的,但是,由于多繼承情況下,可能造成對(duì)基類中某成員的訪問(wèn)出現(xiàn)了不唯一的情況,則稱為對(duì)基類成員訪問(wèn)的二義性問(wèn)題。
實(shí)際上,在上例已經(jīng)出現(xiàn)過(guò)這一問(wèn)題,回憶一下上例中,派生類A的兩基類B1和B2中都有一個(gè)成員函數(shù)print()。如果在派生類中訪問(wèn) print()函數(shù),到底是哪一個(gè)基類的呢?于是出現(xiàn)了二義性。但是在上例中解決了這個(gè)問(wèn)題,其辦法是通過(guò)作用域運(yùn)算符::進(jìn)行了限定。如果不加以限定,則會(huì)出現(xiàn)二義性問(wèn)題。
下面再舉一個(gè)簡(jiǎn)單的例子,對(duì)二義性問(wèn)題進(jìn)行深入討論。例如:
class A
{
public:
void f();
};
class B
{
public:
void f();
void g();
};
class C : public A, public B
{
public:
void g();
void h();
};
如果定義一個(gè)類C的對(duì)象c1:
C c1;
則對(duì)函數(shù)f()的訪問(wèn)
c1.f();
便具有二義性:是訪問(wèn)類A中的f(),還是訪問(wèn)類B中的f()呢?
解決的方法可用前面用過(guò)的成員名限定法來(lái)消除二義性,例如:
c1.A::f();
或者
c1.B::f();
但是,最好的解決辦法是在類C中定義一個(gè)同名成員f(),類C中的f()再根據(jù)需要來(lái)決定調(diào)用A::f(),還是B::f(),還是兩者皆有,這樣,c1.f()將調(diào)用C::f()。
同樣地,類C中成員函數(shù)調(diào)用f()也會(huì)出現(xiàn)二義性問(wèn)題。例如:
viod C::h()
{
f();
}
這里有二義性問(wèn)題,該函數(shù)應(yīng)修改為:
void C::h()
{
A::f();
}
或者
void C::h()
{
B::f();
}
或者
void C::f()
{
A::f();
B::f();
}
另外,在前例中,類B中有一個(gè)成員函數(shù)g(),類C中也有一個(gè)成員函數(shù)g()。這時(shí),
c1.g();
不存在二義性,它是指C::g(),而不是指B::g()。因?yàn)檫@兩個(gè)g()函數(shù),一個(gè)出現(xiàn)在基類B,一個(gè)出現(xiàn)在派生類C,規(guī)定派生類的成員將支配基類中的同名成員。因此,上例中類C中的g()支配類B中的g(),不存在二義性,可選擇支配者的那個(gè)名字。
當(dāng)一個(gè)派生類從多個(gè)基類派生類,而這些基類又有一個(gè)共同的基類,則對(duì)該基類中說(shuō)明的成員進(jìn)行訪問(wèn)時(shí),也可能會(huì)出現(xiàn)二義性。例如:
class A
{
public:
int a;
};
class B1 : public A
{
private:
int b1;
};
class B2 : public A
{
private:
int b2;
};
class C : public B1, public B2
{
public:
int f();
private:
int c;
};
已知:C c1;
下面的兩個(gè)訪問(wèn)都有二義性:
c1.a;
c1.A::a;
而下面的兩個(gè)訪問(wèn)是正確的:
c1.B1::a;
c1.B2::a;
類C的成員函數(shù)f()用如下定義可以消除二義性:
int C::f()
{
retrun B1::a + B2::a;
}
由于二義性的原因,一個(gè)類不可以從同一個(gè)類中直接繼承一次以上,例如:
class A : public B, public B
{
…
}
這是錯(cuò)誤的。