C++多繼承

來源:互聯網
上載者:User
C++多繼承 

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

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

  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的成員包含了基類B中成員以及該類本身的成員。

  多繼承的建構函式

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

  <衍生類別名>(<總參數表>):<基類名1>(<參數表1>),<基類名2>(<參數表2>),…
   <子物件名>(<參數表n+1>),…
    {
     <衍生類別建構函式體>
    }

  其中,<總參數表>中各個參數包含了其後的各個分參數表。

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

  衍生類別建構函式執行順序是先執行所胡基類的建構函式,再執行衍生類別本身建構函式,處於同一層次的各基類建構函式的執行順序取決於定義衍生類別時所指定的各基類順序,與衍生類別建構函式中所定義的成員初始化列表的各項順序無關。也就是說,執行基類建構函式的順序取決於定義衍生類別時基類的順序。可見,衍生類別建構函式的成員初始化列表中各項順序可以任意地排列。
 
  下面通過一個例子來說明衍生類別建構函式的構成及其執行順序。

#include
class B1
{
public:
B1(int i)
{
b1 = i;
cout<<"建構函式 B1." }
void print() { }


private:
int b1;
};

class B2
{
public:
B2(int i)
{
b2 = i;
cout<<"建構函式 B2."}
void print() { }


private:
int b2;
};

class B3
{
public:
B3(int i)
{
b3 = i;
cout<<"建構函式 B3."
}
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."}
void print()
{
B1::print();
B2::print();
}
private:
int a;
B3 bb;
};

void main()
{
A aa(1, 2, 3, 4);
aa.print();
}

  該程式的輸出結果為:

   建構函式 B2.2

   建構函式 B1.1

   建構函式 B3.3

   建構函式 A.4

   1

   2

   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
   {
    …
   }

  這是錯誤的。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.