C++11中的繼承構造函數

時間:2014.06.19

地點:基地

-------------------------------------------------------------------------

一、問題描述

  在繼承體系中,如果派生類想要使用基類的構造函數,需要在構造函數中顯式聲明。如下:

struct A
{
   A(int i){}
};

struct B:A
{
  B(int i):A(i){}
};
在這裏,B派生於A,B

又在構造函數中調用A的構造函數,從而完成構造函數的傳遞。
又比如如下,當B中存在成員變量時:

struct A
{
   A(int i){}
};

struct B:A
{
  B(int i):A(i),d(i){}
  int d;
};
現在派生於A的結構體B包含一個成員變量,我們在初始化基類A的同時也初始化成員d,現在的問題是:假若基類用於擁有爲數衆多的不同版本的構造函數,這樣,在派生類中按上面的思維還得寫很多對應的“透傳”構造函數。如下:

struct A
{
  A(int i) {}
  A(double d,int i){}
  A(float f,int i,const char* c){}
  //...等等系列的構造函數版本
};
struct B:A
{
  B(int i):A(i){}
  B(double d,int i):A(d,i){}
  B(folat f,int i,const char* c):A(f,i,e){}
  //......等等好多個和基類構造函數對應的構造函數
};

很明顯當基類構造函數一多,派生類構造函數的寫法就顯得很累贅,相當不方便。

-------------------------------------------------------------------------

二、問題的解決

  我們可以通過using聲明來完成這個問題的簡化,看一個例子

<pre name="code" class="cpp"><pre name="code" class="cpp">struct Base
{
  void f(double i){
  cout<<"Base:"<<i<<endl;
  }
};

struct Drived:Base
{
  using Base::f;
  void f(int i){
    cout<<"Drived:"<<i<<endl;
  }
};



代碼中基類和派生類都聲明瞭同名的函數f,但派生類中辦法和基類的版本不同,這裏使用using聲明,說明派生類中也使用基類版本的函數f,這樣派生類中就擁有兩個f函數的版本了。在這裏需要說明的是,如果沒有使用using聲明繼承父類同名函數,那麼派生類中定義的f函數將會屏蔽父類的f函數,當然若派生類根本就沒有定義這個f同名函數,還會選擇用基類的f函數。
這種方法,我們一樣可遷移到構造函數的繼承上,即派生類可以通過using語句聲明要在子類中繼承基類的所有構造函數。如下:

struct A
{
  A(int i) {}
  A(double d,int i){}
  A(float f,int i,const char* c){}
  //...等等系列的構造函數版本
};
struct B:A
{
  using A::A;
  //關於基類各構造函數的繼承一句話搞定
  //......
};
現在,通過using A::A的聲明,將基類中的構造函數全繼承到派生類中,更巧妙的是,這是隱式聲明繼承的,即如果一個繼承構造函數不被相關的代碼使用,編譯器不會爲之產生真正的函數代碼,這樣比透傳基類各種構造函數更加節省目標代碼空間。

但此時還有一個問題:

當使用using語句繼承基類構造函數時,派生類無法對類自身定義的新的類成員進行初始化,我們可使用類成員的初始化表達式,爲派生類成員設定一個默認初始值。比如:

struct A
{
  A(int i) {}
  A(double d,int i){}
  A(float f,int i,const char* c){}
  //...等等系列的構造函數版本
};
struct B:A
{
  using A::A;
  int d{0};
};

注意:

  1.對於繼承構造函數來說,參數的默認值是不會被繼承的,而且,默認值會 導致基類產生多個構造函數版本(即參數從後一直往前面減,直到包含無參構造函數,當然如果是默認複製構造函數也包括在內),這些函數版本都會被派生類繼承。

  2.繼承構造函數中的衝突處理:當派生類擁有多個基類時,多個基類中的部分構造函數可能導致派生類中的繼承構造函數的函數名。參數都相同,那麼繼承類中的繼承構造函數將導致不合法的派生類代碼,比如:

struct A
{
  A(int){}
};
struct B
{
  B(int){}
};
struct C:A,B
{
  using A::A;
  using B::B;
};
  在這裏將導致派生類中的繼承構造函數發生衝突,一個解決的辦法就是顯式定喲繼承類的衝突構造函數,阻止隱式生成相應的繼承構造函數,以免發生衝突。

struct C:A,B
{
  using A::A;
  using B::B;
  C(int){}
};
3.如果基類的構造函數被聲明爲私有構造函數或者派生類是從基類虛繼承的,那麼就不能在派生類中聲明繼承構造函數。

4.如果一旦使用了繼承構造函數,編譯器就不會爲派生類生成默認構造函數。這樣,我們得注意繼承構造函數無參版本是不是有需要。






發佈了193 篇原創文章 · 獲贊 53 · 訪問量 36萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章