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.假設一旦使用了繼承構造函數,編譯器就不會為派生類生成默認構造函數。這樣,我們得注意繼承構造函數無參版本號是不是有須要。







免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM