C++11の継承コンストラクタ

3436 ワード

時間: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;
};

ここでは派生クラスの継承構造関数に衝突が発生し、1つの解決策は明示的に継承クラスの衝突構造関数を決定し、暗黙的に相応の継承構造関数を生成することを阻止し、衝突が発生しないようにすることである.
struct C:A,B
{
  using A::A;
  using B::B;
  C(int){}
};

3.ベースクラスのコンストラクション関数がプライベートコンストラクション関数または派生クラスがベースクラスからダミー継承されていると宣言された場合、派生クラスで継承コンストラクション関数を宣言することはできません.
4.継承コンストラクタを使用すると、コンパイラは派生クラスのデフォルトコンストラクタを生成しません.このように、コンストラクション関数の非パラメトリックバージョンを継承する必要があるかどうかに注意しなければなりません.