C 多繼承中的二義性

2021-12-29 20:50:25 字數 3788 閱讀 8867

多繼承可以看作是單繼承的擴充套件。所謂多繼承是指派生類具有多個基類,派生類與每個基類之間的關係仍可看作是乙個單繼承。

多繼承下派生類的定義格式如下:

class 派生類:繼承方式1>基類名1>,繼承方式2>基類名2>,…

派生類類

其中,繼承方式1>,繼承方式2>,…是三種繼承方式:public、private、protected之一。例如:

class a

class b

class c : public a, public b

其中,派生類c具有兩個基類(類a和類b),因此,類c是多繼承的。按照繼承的規定,派生類c的成員包含了基類a, b中成員以及該類本身的成員。

多繼承的建構函式

在多繼承的情況下,派生類的建構函式格式如下:

派生類(總引數):基類名1>(參數列1>),基類名2>(參數列2>),…

子物件(參數列n+1>),…

派生類建構函式

其中,總引數中各個引數包含了其後的各個分參數列。

多繼承下派生類的建構函式與單繼承下派生類建構函式相似,它必須同時負責該派生類所有基類建構函式的呼叫。同時,派生類的引數個數必須包含完成所有基類初始化所需的引數個數。

派生類建構函式執行順序是先執行所屬基類的建構函式,再執行派生類本身建構函式,處於同一層次的各基類建構函式的執行順序取決於定義派生類時所指定的各基類順序,與派生類建構函式中所定義的成員初始化列表的各項順序無關。也就是說,執行基類建構函式的順序取決於定義派生類時基類的順序。可見,派生類建構函式的成員初始化列表中各項順序可以任意地排列。

下面通過乙個例子來說明派生類建構函式的構成及其執行順序。

#include

class b1

public:

b1(int i)

b1 = i;

cout《"建構函式 b1."《i《 endl;

void print()

cout《"b1.print()"《b1《endl;

private:

int b1;

class b2

public:

b2(int i)

b2 = i;

cout《"建構函式 b2."《i《 endl;

void print()

cout《"b2.print()"《b2《endl;

private:

int b2;

class b3

public:

b3(int i)

b3 = i;

cout《"建構函式 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《"建構函式 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();

該程式的輸出結果為:

建構函式 b2.2

建構函式 b1.1

建構函式 b3.3

建構函式 a.4

b1.print()。1

b2.print()2

a.print()4, 3

在該程式中,作用域運算子::用於解決作用域衝突的問題。在派生類a中的print()函式的定義中,使用了b1::print;和b2::print();語句分別指明呼叫哪乙個類中的print()函式,這種用法應該學會。

二義性問題

一般說來,在派生類中對基類成員的訪問應該是唯一的,但是,由於多繼承情況下,可能造成對基類中某成員的訪問出現了不唯一的情況,則稱為對基類成員訪問的二義性問題。

實際上,在上例已經出現過這一問題,回憶一下上例中,派生類a的兩基類b1和b2中都有乙個成員函式print()。如果在派生類中訪問 print()函式,到底是哪乙個基類的呢?於是出現了二義性。但是在上例中解決了這個問題,其辦法是通過作用域運算子::進行了限定。如果不加以限定,則會出現二義性問題。

下面再舉乙個簡單的例子,對二義性問題進行深入討論。例如:

class a

public:

void f();

class b

public:

void f();

void g();

class c : public a, public b

public:

void g();

void h();

如果定義乙個類c的物件c1:

c c1;

則對函式f()的訪問

c1.f();

便具有二義性:是訪問類a中的f(),還是訪問類b中的f()呢?

解決的方法可用前面用過的成員名限定法來消除二義性,例如:

c1.a::f();

或者c1.b::f();

但是,最好的解決辦法是在類c中定義乙個同名成員f(),類c中的f()再根據需要來決定呼叫a::f(),還是b::f(),還是兩者皆有,這樣,c1.f()將呼叫c::f()。

同樣地,類c中成員函式呼叫f()也會出現二義性問題。例如:

viod c::h()

f();

這裡有二義性問題,該函式應修改為:

void c::h()

a::f();

或者void c::h()

b::f();

或者void c::f()

a::f();

b::f();

另外,在前例中,類b中有乙個成員函式g(),類c中也有乙個成員函式g()。這時,

c1.g();

不存在二義性,它是指c::g(),而不是指b::g()。因為這兩個g()函式,乙個出現在基類b,乙個出現在派生類c,規定派生類的成員將支配基類中的同名成員。因此,上例中類c中的g()支配類b中的g(),不存在二義性,可選擇支配者的那個名字。

當乙個派生類從多個基類派生類,而這些基類又有乙個共同的基類,則對該基類中說明的成員進行訪問時,也可能會出現二義性。例如:

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;

下面的兩個訪問都有二義性:

c1.a;

c1.a::a;

而下面的兩個訪問是正確的:

c1.b1::a;

c1.b2::a;

類c的成員函式f()用如下定義可以消除二義性:

int c::f()

retrun b1::a + b2::a;

由於二義性的原因,乙個類不可以從同乙個類中直接繼承一次以上,例如:

class a : public b, public b

這是錯誤的。

摘自  十分愛

C 多繼承的二義性

單繼承 派生類只從乙個基類派生 多繼承 派生類從多個基類派生 多重派生 有乙個基類派生出多個不同的派生類 多層派生 派生類又作為基類,繼續派生出新的類 多繼承可以看作是單繼承的擴充套件。所謂多繼承是指派生類具有多個基類,派生類與每個基類之間的關係仍可看作是乙個單繼承。多繼承下派生類的定義格式如下 c...

C 多繼承的二義性

多繼承可以看作是單繼承的擴充套件。所謂多繼承是指派生類具有多個基類,派生類與每個基類之間的關係仍可看作是乙個單繼承。多繼承下派生類的定義格式如下 class 派生類名 繼承方式1 基類名1 繼承方式2 基類名2 其中,繼承方式1 繼承方式2 是三種繼承方式 public private protec...

C 多繼承的二義性

多繼承可以看作是單繼承的擴充套件。所謂多繼承是指派生類具有多個基類,派生類與每個基類之間的關係仍可看作是乙個單繼承。多繼承下派生類的定義格式如下 class 派生類名 繼承方式1 基類名1 繼承方式2 基類名2 其中,繼承方式1 繼承方式2 是三種繼承方式 public private protec...