C ++ - サブクラス建設、破壊、コピー、多重継承、継承、オーバーロード名の隠蔽、石材、仮想継承、ポリモーフィズム(Day9)

、サブクラスは、コンフィギュレーション、破壊、コピーを継承します(セクションDay8を参照してください)

1、コピーコンストラクタクラス

{基本クラスの
パブリック
  ベース(ボイド):M_I(0){ // 引数なしでコンストラクタ        }   ベース(int型 I):M_I(I){ // パラメータが設定した   }   〜ベース(ボイド){}   int型M_Iを; }; クラス派生:民間ベース{ パブリック   派生(無効){}   派生(int型 I、int型J):ベース(I)、M_I(J){}   // 派生(派生&ことのconst):M_I(that.m_i){ } // サブクラスは、この呼び出しの割り当てをコピーするが、記述された基本クラスを初期化する方法がないため、意味的に無コピーように、基本クラスの初期設定パラメータを形成しません   (派生CONSTが M_I(that.m_i)、ベース(その){}:派生&その)// 初期化されますが、ベースを使用できますが:: M_Iが、M_Iがプライベートメンバーである場合、アクセスすることはできません   // ベース(つまり)の呼び出しをコピーコンストラクタベースクラスサブオブジェクト、すなわちシェイプアップ、暗黙ベースオブジェクトに変換されること、初期化され   〜派生(ボイド){}   int型M_I }; // メイン関数 導出D1(100200である); Deribved D2(D1); // コピーコンストラクタ

 

上記のように、以下の注意事項があります。

1)コピーコンストラクタを定義していないサブクラスは、コンパイラが自動的にサブクラスコピーのデフォルトコンストラクタを提供します、コピー機能は、自動的に基底クラスのコンストラクタ、基本クラスのサブオブジェクトの初期化を呼び出します。

2)サブオブジェクトのコピーコンストラクタ関数を定義して、あなたはサブクラスでテーブルを初期化する必要が明示的にBenQはサブオブジェクトがコンストラクタをコピーするか、または基本クラスのサブオブジェクトは、パラメータを初期化する方法はないだろうと述べました。

 

2、コピーサブクラスの割り当て

  上記のコードの場合:

D3派生; 

D3 = D1; // コピー代入

 

場合は、サブオブジェクトのディープコピーとメンバーを必要としないで、あなたがコピー代入機能をカスタマイズしたい場合、あなたは、デフォルトのコピー割り当て機能を使用することができます。

&派生演算子 =(CONSTがその&派生){ // のみコピー代入サブオブジェクトを考慮し、パラメータなしの基本クラスのサブオブジェクトのコンストラクタが呼び出される

  IF(=すなわち&!){ 

    M_I = that.m_i;  
    // ベース:: M_I = that.Base :: M_I;不完全 
    ベース:: 演算子 =(その); // 上方アート
  } 

  戻り * この; 

}

上記のコードについては、次のとおりです。

1)サブクラス代入演算子のコピー機能を定義していない、コンパイラはデフォルトの代入演算子コピーの機能を提供し、サブクラスは、自動的に、基本クラスのコピーのための基本クラスのサブオブジェクトを代入演算子のコピーを呼び出します。

2)コピー割り当て機能サブクラス定義は、明示的なコピーの割り当ては基本クラスを呼び出す必要とする場合、基本クラスのサブオブジェクトを割り当てること。これは、通常のコピー代入できない場合があります。

 

第二に、多重継承

1、概念と文法

  サブクラスは、基底クラス、そのような継承複数の多重継承と呼ばれる同じ時間を継承します。初期化リストを書いて多重継承はday8は一部を継承参照してください。

  シェープアップ

  多重継承と上方形状は、単一及び単一継承以下同じ規則が、各サブオブジェクトの基本クラスの開始アドレスは、それに応じてオフセット基本クラスのサブオブジェクトのアドレスの順序とサイズによって継承されます。

 

2、名前の競合

  場合は、同じ名前のメンバーは、紛争の形成をサブクラスが存在します。

例:

クラスA { 

パブリック

  ボイドのfoo(ボイド){ 

  } 

}。

クラスB { 

パブリック

  ボイドのfoo(int型I){ 

  } 

}。



クラス C:パブリック A、公衆   B {}。

 

INTメイン(ボイド){ 

  C C; 

  // 異なる機能は、クラスAとBの範囲にはFoo、重い負荷を構成することができないので、曖昧さが衝突することができ

  C.fooを();  
  C.fooは(10 ); 

  戻り 0 ; 

}

 

呼び出すために、次のように進んで

INTメイン(ボイド){ 

  C C; 

  // 異なる機能は、クラスAとBの範囲にはFoo、重い負荷を構成することができないので、それは、オーバーロードされた内部Cに導入することができるキーワードを使用して、不一致を競合するが、これら2つの関数は重負荷条件満たさなければならない

  カリフォルニア州::のfoo();  

  CB :: FOO(10 ); 

  戻り 0 ; 

}

 

例:

クラスA { 

パブリック

  ボイドのfoo(ボイド){ 

  } 
  
  のtypedef int型M_DATA; 
}; クラス B { パブリック:   ボイドのfoo(int型I){}
  int型M_DATAを; 

}; 



クラス C:パブリック A、公衆  B { 
  
}; 
//もし次のようにスコープ修飾子なしで、2がオフM_DATAする必要はありませんが、直接違反エラーを報告し、正しい使用がある:

  CのC、

  CB :: M_DATA = 10 ;  

  のcA :: M_DATA NUM = 10 ;

  競合がメンバ関数で、重い負荷条件を満たし、同じ名前の競合を解決するために、サブクラスの導入にキーワードを使用している場合、通常は「クラス名::」モードを使用して、名前の競合を解決するための一般的な方法を要約すると。

 

3、石工の継承 

                                                                           

 

 

  そのようなサブクラスの複数の上記のようにこのように閉ループを形成する基底クラスと同じクラスZuxianチから継承ベースクラスは、継承石工継承と呼ばれる継承。

クラスA { 

パブリック

  (INT データ):M_DATA(データ){} 

保護

  INT M_DATA。

}。

クラス B:パブリックA { 

パブリック

  B(INT データ):(データ){} 

  ボイド 集合整数データ){ 

    M_DATA = データ。

  } 

}。

クラス C:公共A { 

パブリック

  C(INT データ):(データ){} 

  INT  GETボイド){ 

    戻りM_DATA。

  } 

}; 

クラス D:パブリック B、公衆C { 

パブリック

  D(INT データ):B(データ)、C(データ){} 

};              

int型メイン(ボイド

{ 

  D D(100 ); 

  D。SET200であります

  。D GET (); 

  // 値はM_DATAが200ないM_DATAはなく、変化しないが、二つのM_DATAの存在、の存在下でベースサブオブジェクトB、およびCの存在下で取得基本クラスのサブオブジェクト。このようなコードを維持することは困難です。

}

  

 

  したがって、上記のように、(A)一般的なサブオブジェクトの基本クラスのメンバー中間体(M_DATA)のサブクラスの複数の中間収束サブクラスのサブクラス(D)オブジェクト、複数のインスタンスの複数の継承します。なぜなら、異なる遺伝経路の収束サブクラス(D)オブジェクト、公衆基地クラスのメンバーへのアクセスにより、不整合が生じます。

4、仮想継承

  上記2つの同じデータに、コードの保守は、仮想継承を解決することができる使用して、比較的複雑です。仮想継承によって収束サブクラスのオブジェクトに共通のベースクラスサブオブジェクト一意のインスタンスを可能にし、すべてのサブクラスで共有中間体。でも、異なる経路に沿って連続しても、共通の基底クラスのメンバへのアクセスは一致している必要があります。

 

class A{

public:

  A(int data):m_data(data){}

protected:

  int m_data;

};

class B:virtual public A{//虚继承

public:

  B(int data):A(data){}

  void set(int data){

    m_data=data;

  }

};

class C:virtual public A{//虚继承

public:

  C(int data):A(data){}

  int get(void){

    return m_data;

  }

};

class D:public B,public C{

public:

  D(int data):B(data),C(data),A(data){}//由D来指定A的初始化方式

};              

int main(void)

{

  D d(100);

  d.set(200);

  d.get();

  //m_data获取的m_data的值并不是200,但是并不是没有改变,而是存在两份m_data,一份存在于B的基类子对象,一份存在与C的基类子对象。这样是的代码不易维护。

}

 

 虚继承语法:

  (1)虚继承中,使用关键字virtual

  (2)位于继承链最末端的汇聚子类构造函数负责构造虚基类(A)子对象

  (3)但是虚基类的所有子类(B、C、D)都必须在其构造函数中显式的指明该虚基类子对象的初始化方式,否则编译器将会以无参的构造方式初始化基类子对象。

 

 虚继承原理:

  如果在A、B、C、D的构造函数中输出构造对象时的起始地址,会发现,如上的结构,D构造的对象与B起始地址相同。实际只存在一份m_data,在访问时通过B,C内部的指针(虚继承产生的虚指针)加上偏移量,完成对一份数据的访问

 

三、多态(重要)

1、引入

  如果将基类中的某个成员函数声明为虚函数,那么子类中的与基类中具有相同原型的成员函数也是虚函数,并且对基类中的版本形成覆盖。这是通过指向子类对象的基类指针,或者通过引用子类对象的的基类引用,调用虚函数,实际被调用的将是子类中的覆盖版本,而不是基类中原始版本,这种语法现象称为多态。

子类与父类中相同原型的虚函数将形成覆盖关系,即子类覆盖父类的虚函数,并且子类的这个函数无论是否加virtual关键字都是虚函数

class Shape{

public:

  Shape(int x,int y):m_x(x),m_y(y){

    
  }

  virtual void draw(void){}

protected:

  int m_x;

  int m_y;

};



class recr;public Shape{

public:

  Rect(int x,int y,int w,int h):

    Shape(x,y),m_w(w),m_h(h){}
  void draw(void){

  //需要只要矩形的信息

  }

private:

  int m_w;

  int m_h;

};



class Circle:public Shape{

public:

  Circle(int x,int y,int r):

    Shape(x,y),m_r(r){}
  void draw(void){

  //需要知道圆形的信息

  }

private:

  int m_r; 

};



void render(Shape* shapes[]){

  for(int i=0;shapes[i];i++){

    shape[i]->draw();//如果Shape中draw()不加virtual关键字,就只能调用基类Shape中的draw()函数,修改后体现了多态
    //调用的函数不再有指针的类型决定,而是有实际的目标对象类型决定
  }

}

int main(void){

  Shape* shapes[1024]={NULL};//隐式向上造型

  shapes[0]=new Rect(1,2,3,4);

  shapes[1]=new Circle(5,6,7);

  //。。。

  render(shapes);

  return 0;

}

2、虚函数构成覆盖的条件

1)只有类的普通成员函数才能声明为虚函数(析构函数可以声明为虚函数),全局函数,静态函数,静态成员函数,构造函数不能声明为虚函数

2)只有基类中以virtual修饰的成员函数才能作为虚函数被子类覆盖,而与子类的virtual关键字无关

3)虚函数在子类和基类中必须具有相同的函数原型,即函数名,形参表,常属性等。注意这与重载不同,这里强调的是覆盖。同时,一般返回值也要求相同,但是如果子类的函数和基类中的虚函数返回值具有继承关系(引用或者指针或者类类型),并且满足上述条件,也可以完成覆盖(类型协变)。

 

おすすめ

転載: www.cnblogs.com/ptfe/p/11296170.html