C++学習心得(5)継承と派生

26779 ワード

C++学習心得(5)継承と派生
-from譚浩強C++オブジェクト向けプログラミング(第1版)2014/10/9オブジェクト向けプログラミングの4つの主な特徴:抽象、パッケージ、継承、多態性
5.1継承と派生の概念
C++では、継承は既存のクラスに基づいて新しいクラスを構築します.新しいクラスは既存のクラスからその既存の特性を獲得し,この現象を継承と呼ぶ.既存のクラスから新しいサブクラスが生成され、クラスの派生と呼ばれます.1つの派生クラスが1つのベースクラスからのみ派生することを単一継承と呼ぶ.1つの派生クラスには、複数のベースクラスが複数継承と呼ばれます.
5.2派生クラスの宣言方式
派生クラスを宣言する一般的な形式は、次のとおりです.
class     : [    ]    
{
             ;
};

継承方法は次のとおりです.
  • public、共通の;
  • private、プライベート;
  • protected、保護されています.
  • -
    5.3派生類の構成
    派生クラスの3つの部分の作業を構築します.
  • ベースクラスからメンバーを受信する.ベースクラスのすべてのメンバーを受信しますが、コンストラクション関数とコンストラクション関数は含まれません.
  • ベースクラスから受信したメンバーを調整します.継承方法を指定してアクセス属性を調整します.派生クラスでベースクラスのメンバーと同じ名前のメンバーを宣言すると、新しいメンバーはベースクラスの同じ名前のメンバーを上書きします.
  • 派生クラスが宣言されたときにメンバーが追加されます.構造関数と解析関数を自分で作成します.

  • 5.4派生クラスメンバーのアクセス属性
    考慮すべき状況:
  • 派生クラスのメンバー関数はベースクラスのメンバーにアクセスする.
  • 派生クラスの外でベースクラスのメンバーにアクセスする.3つの継承方法:
  • 共通継承ベースクラスの共通メンバーと保護メンバーは、既存のアクセス属性を保持し、そのプライベートメンバーはベースクラスにプライベートであり、派生クラスはアクセスできません.
  • プライベート継承ベースクラスの共通メンバーと保護メンバーは派生プライベートメンバーとなり、派生クラスメンバー関数のみがアクセスでき、派生クラス外はアクセスできず、そのプライベートメンバーはベースクラスにプライベートであり、派生クラスはアクセスできない.
  • 保護継承共通メンバーおよび保護メンバーは派生クラス保護メンバーとなり、そのプライベートメンバーはベースクラスにプライベートであり、派生クラスはアクセスできません.


  • 保護メンバーとは、外部から引用されてはいけないが、派生類のメンバーに引用されることができ、遺産に似ているという意味だ.新規の継承
    関数データ
    5.4.1共通相続
    class Student                                        //    
    {
        public:
           voidget_value()
           {
               cin>> num >> name >> sex;
           }
           voiddisplay()
           {
               cout<< "num: " << num << endl;
               cout<< "name: " << name << endl;
               cout<< "sex: " << sex << endl;
           }
        private:
           intnum;
           stringname;
           charsex;
    };
    
    class Student1: public Student                       //  public       Student1
    {
        public:
           voiddisplay_1()
           {
               //cout<< "num: " << num << endl;   
               //cout<< "name: " << name << endl;   
               //cout<< "sex: " << sex << endl;   
               cout<< "age: " << age << endl;
               cout<< "address: " << addr << endl;
           }
        private:
           intage;
           stringaddr;
    };
    
    class Student1: public Student
    {
        public:
           voiddisplay_1()
           {
               cout<< "age: " << age << endl;
               cout<< "address: " << addr << endl;
           }
        private:
           intage;
           stringaddr;
    };

    5.4.2私有相続
    class Student1: private Student
    {
        public:
           voiddisplay_1()
           {
               cout<< "age: " << age << endl;
               cout<< "address: " << addr << endl;
           }
        private:
           intage;
           stringaddr;
    };

    派生クラスオブジェクトからプライベートベースクラスから継承されたメンバーを参照することはできません.
    5.4.3保護メンバーと保護継承
    クラスのユーザーの観点から、保護メンバーはプライベートメンバーと等価ですが、保護メンバーは派生クラスのメンバー関数で参照できます.
    直接派生クラスでのプライベート継承と保護継承の比較は、クラス外ではメンバーにアクセスできませんが、派生クラスではメンバー関数を使用してベースクラスの共通メンバーと保護メンバーにアクセスできます.派生を続ける場合(共通継承で派生する場合)、元のプライベートベースクラスのメンバーは、新しい派生クラスでアクセスできないメンバーになります.//
    保護メンバーとプライベートメンバーの違いは、保護メンバーのアクセス範囲を派生クラス関数に広げることです.
    プライベート継承と保護継承は一般的には使用されません.
    5.4.4マルチレベル派生時のアクセス属性
    直接派生クラス、間接派生クラス、直接ベースクラス、間接ベースクラスは、マルチレベル派生時に共通継承方式を採用し、最終レベル派生クラスまでベースクラスの共通メンバーと保護メンバーにアクセスできます.
    5.5派生クラスの構造関数と構造関数
    派生クラス構築関数を実行するときに、派生クラスのデータメンバーとベースクラスのデータメンバーを同時に初期化します.問題を解決する考え方は,派生クラスの構造関数を実行する際に,ベースクラスの構造関数を呼び出すことである.
    5.5.1簡単な派生クラスの構築関数
    #include <iostream>
    #include <string>
    using namespace std;
    
    class Student
    {
        public:
           Student(intn, string nam, char s)               //       
           {
               num= n;
               name= nam;
               sex= s;
           }
           ~Student() {}                                 //    
        protected:
           intnum;
           stringname;
           charsex;
    };
    
    class Student1: public Student                          //        Student1
    {
        public:
           Student1(intn, string nam, char s, int a, string ad);
    
           voidshow()
           {
               cout<< "num: " << num << endl;
               cout<< "name: " << name << endl;
               cout<< "sex: " << sex << endl;
               cout<< "age: " << age << endl;
               cout<< "address: " << addr << endl;
           }
           ~Student1(){}
        private:
           intage;
           stringaddr;
    };
    
    Student1::Student1(int n, string nam, char s, inta, string ad):Student(n, nam, s), age(a), addr(ad) {}
    
    int main()
    {
        Student1stud1(10010, "Wang_li", 'f', 19, "115 BeijingRoad,Shanghai");
        Student1stud2(10011, "Zhang_fun", 'm', 21, "213 Shanghai Road,Beijing");
        stud1.show();
        stud2.show();
    
        return 0;
    }

    重要な部分は、宣言:Student1(intn, string nam, char s, int a, string ad);定義:Student1::Student1(intn, string nam, char s, int a, string ad):Student(n, nam, s), age(a), addr(ad) {}もこのように書くことができます.
    Student1(int n, string nam, char s, int a, stringad):Student(n, nam, s)
    {
        age = a;
        addr = ad;
    }

    一般的な形式は次のとおりです.
            (     ):       (    )
    {                ; }

    ベースクラスコンストラクション関数名(パラメータリスト)には、パラメータタイプを含まないパラメータ名のみが含まれます.ここではベースクラスコンストラクション関数を定義するのではなく、ベースクラスコンストラクション関数を呼び出すため、これらのパラメータはパラメータではなく実パラメータです.
                       ,                。          。
    

    ベースクラスコンストラクション関数を呼び出すときの実パラメータは、派生クラスコンストラクション関数の総パラメータリストから得られます.定数またはグローバル変数を直接使用することもできます.Student1(string nam, char s, int a, stringad):Student(10010, nam, s)
    5.5.2サブオブジェクトを持つ派生クラスの構築関数
    サブオブジェクト、すなわちオブジェクト内のオブジェクトと呼ばれる埋め込みオブジェクト.構造体タイプのメンバーは、構造体変数であってもよい.
    #include<iostream>
    #include<string>
    using namespacestd;
    
    class Student
    {
        public:
           Student(int n, string nam)
           {
               num = n;
               name = nam;
           }
           void display()
           {
               cout << "num: "<< num << endl << "name: " << name <<endl;
           }
        protected:
           int num;
           string name;
    };
    
    class Student1:public Student
    {
        public:
          Student1(int n, string nam, int nl, stringnam1, int a, string ad):Student(n, nam), monitor(nl, nam1)
           {
               age = a;
               addr = ad;
           }
           void show()
           {
               cout << "This Student is:" << endl;
               display();
               cout << "age: "<< age << endl;
               cout << "address: "<< addr << endl << endl;
           }
           void show_monitor()
           {
               cout << endl <<"Class monitor is : " << endl;
               monitor.display();
           }
        private:
           Student monitor;
           int age;
           string addr;
    };
    
    int main()
    {
        Student1 stud1(10010, "Wang_Li",10011, "Li_sun", 19, "115 BEIJING");
        stud1.show();
        stud1.show_monitor();
    
        return 0;
    }

    サブオブジェクトの初期化は、派生クラスを作成するときに派生クラス構築関数を呼び出すことによって実現されます.派生クラス構築関数のタスクには、次の3つのセクションがあります.
  • ベースクラスデータメンバーを初期化する.
  • オブジェクトデータメンバーを初期化する.
  • 派生クラスデータメンバーを初期化する.
  • Student1(int n,string nam, int nl, string nam1, int a, string ad):Student(n, nam), monitor(nl,nam1)
    {
        age = a;
        addr = ad;
    }

    派生クラス構築関数を定義する一般的な形式は次のとおりです.
            (     ):       (    ),    (    )
    {                ; }

    実行順序は、ベースクラス、サブオブジェクト、派生クラスコンパイルシステムは、パラメータ順序ではなく同じパラメータ名に基づいて伝達関係を決定します.
    5.5.3多層派生時の構造関数
    #include<iostream>
    #include<string>
    using namespacestd;
    
    class Student
    {
        public:
           Student(int n, string nam)
           {
               num = n;
               name = nam;
           }
           void display()
           {
               cout << "num: "<< num << endl;
               cout << "name: "<< name << endl;
           }
        protected:
           int num;
           string name;
    };
    
    class Student1:public Student
    {
        public:
           Student1(int n, string nam, inta):Student(n, nam)
           {
               age = a;
           }
           void show()
           {
               display();
               cout << "age: "<< age << endl;
           }
        private:
           int age;
    };
    
    class Student2:public Student1
    {
        public:
           Student2(int n, string nam, int a, ints):Student1(n, nam, a)
           {
               score = s;
           }
           void show_all()
           {
               show();
               cout << "score: "<< score << endl;
           }
        private:
           int score;
    };
    
    int main()
    {
        Student2 stud(10010, "Li", 17,89);
        stud.show_all();
    
        return 0;
    }

    注意:
    ベースクラスのコンストラクション関数の最初の部分:
    Student(int n, string nam)

    派生クラスStudent 1のコンストラクタヘッダ
    Student1(int n, string nam, int a):Student(n, nam)

    派生クラスStudent 2のコンストラクタヘッダ
    Student2(int n, string nam, int a, int s):Student1(n,nam, a)

    自分の直接ベースクラスのコンストラクション関数を書くだけでいい!!!
    5.5.4派生クラス構築関数の特殊形式
  • 派生クラスの新規メンバーを初期化する必要がない場合(新規はまったくありません!)、派生クラスコンストラクション関数の関数体は、空であってもよく、すなわち、コンストラクション関数は空の関数であってもよい.
  • ベースクラスにコンストラクション関数が定義されていないか、パラメータのないコンストラクション関数が定義されていない場合は、派生クラスコンストラクション関数を定義するときにベースクラスコンストラクション関数を書かなくてもよい.(派生クラスコンストラクション関数には、ベースクラスコンストラクション関数にパラメータを渡すタスクはありません!パラメータのないベースクラスコンストラクション関数も渡す必要はありません.)ベースクラスがパラメータなしのコンストラクション関数を定義している場合、パラメータが定義されているコンストラクション関数(コンストラクション関数が再ロードされています...)があります.じゃ、勝手に伝えてください.
  • 3.
    5.5.5派生クラスの構造関数
    派生クラス独自の構造関数を実行し、サブオブジェクトを実行し、最後にベースクラスを実行します.
    5.6多重継承
    5.6.1多重継承を宣言する方法
    class D: public A,private B, protected C
    {
         D      ;
    }

    5.6.2多重継承派生クラスの構造関数
    初期化関数リストに複数のベースクラス構築関数を含める
             (     ):  1    (    ),  2    (    ),  3    (    )
    {                 ; }

    問題:多重継承では、異なるベースクラスから重複するデータが継承されます.
    5.6.3多重継承による二義性問題
    A.2つのベースクラスには同名のメンバーがいる.
    class A
    {
        public:
           int a;
           voiddisplay();
    };
    
    class B
    {
        public:
           int a;
           voiddisplay();
    };
    
    class C: public A, public B
    {
        public:
           int b;
           voidshow();
    };
    
    c1.A::a = 3;
    c1.A::display();

    B.2つのベースクラスと派生クラスの3つには同名のメンバーがいる.
    class C: public A, public B
    {
        int a;
        voiddisplay();
    };
    
    c1.A::a = 3;
    c1.A::display();

    ベースクラスの同名メンバーは派生クラスでマスクされます.C.クラスAとクラスBが同一のベースクラスから派生している場合
    class N
    {
        public:
           int a;
           voiddisplay()
           {
               cout<< "A::a = " << a << endl;
           }
    };
    
    class A: public A
    {
        public:
           int a1;
    };
    
    class B: public B
    {
        public:
           int a2;
    };
    
    class C: public A, public B
    {
        public:
           int a3;
           voidshow()
           {
               cout<< "a3 = " << a3 << endl;
           }
    };

    クラスNの直接派生クラス名によって、アクセスするクラスNのどの派生クラスのベースクラスメンバーであるかを示すべきである.
    c1.A::a = 3;
    c1.A::display();

    5.6.4ダミーベースクラス
  • 虚基類の作用
  • C++は、間接的な共通ベースクラスを継承するときに1つのメンバーのみが保持されるように、ダミーベースクラスのメソッドを提供します.ダミーベースクラスは、ベースクラスを宣言するときに宣言されるのではなく、派生クラスを宣言するときに継承方法を指定するときに宣言されます.1つのベースクラスは、1つの派生クラスを生成するときに虚ベースクラスとして機能し、別の派生クラスを生成するときに虚ベースクラスとして機能しないからです.ダミーベースクラスを宣言する一般的な形式は、次のとおりです.
    classvirtual        

    このベースクラスのすべての直接派生クラスで虚ベースクラスとして宣言する必要があります.2.ダミーベースクラスの初期化最後の派生クラスでは、その直接ベースクラスの初期化だけでなく、ダミーベースクラスの初期化も担当します.
    #include<iostream>
    #include<string>
    
    using namespacestd;
    
    class Person
    {
        public:
           Person(string nam, char s, int a)
           {
               name = nam;
               sex = s;
               age = a;
           }
        protected:
           string name;
           char sex;
           int age;
    };
    
    class Teacher:virtual public Person
    {
        public:
           Teacher(string nam, char s, int a, stringt):Person(nam, s, a)
           {
               title= t;
           }
        protected:
           string title;
    };
    
    class Student:virtual public Person
    {
        public:
           Student(string nam, char s, int a, floatsco):Person(nam, s, a), score(sco) {}
        protected:
           float score;
    };
    
    class Graduate:public Teacher, public Student
    {
        public:
           Graduate(string nam, char s, int a,string t, float sco, float w):Person(nam, s, a), Teacher(nam, s, a, t),Student(nam, s, a, sco), wage(w) {}
           void show()
           {
               cout << "name: "<< name << endl;
               cout << "age: "<< age << endl;
               cout << "sex: "<< sex << endl;
               cout << "score: "<< score << endl;
               cout << "title: "<< title << endl;
               cout << "wage: "<< wage << endl;
           }
        private:
           float wage;
    };
    
    int main()
    {
        Graduate grad1("wang_li", 'f', 24,"assistant", 89.5, 12345.5);
        grad1.show();
    
        return 0;
    }

    5.7ベースクラスと派生クラスの変換
    共通継承のみがベースクラスの真のサブタイプであり、ベースクラスの機能を完全に継承します.異なるデータ型間の自動変換と付与は、付与互換性と呼ばれます.
  • 派生クラスオブジェクトは、ベースクラスオブジェクトに値を付与することができる.一方向的、不可逆的
  • 派生クラスオブジェクトは、ベースクラスオブジェクトからベースクラスオブジェクトへの参照に代わって付与または初期化することができる.
  • 関数の実パラメータがベースクラスオブジェクトまたはベースクラスオブジェクトの参照である場合、対応する実パラメータはサブクラスオブジェクトを使用することができる.
  • ベースクラスオブジェクトを指すポインタ変数は、派生クラスオブジェクトを指すこともできる.
  • #include<iostream>
    #include<string>
    using namespacestd;
    
    class Student
    {
        public:
           Student(int, string, float);
           void display();
        private:
           int num;
           string name;
           float score;
    };
    
    Student::Student(intn, string nam, float s):num(n), name(nam), score(s) {}
    
    voidStudent::display()
    {
        cout << endl << "num:" << num << endl;
        cout << "name: " <<name << endl;
        cout << "score: " <<score << endl;
    }
    
    class Graduate:public Student
    {
        public:
           Graduate(int, string, float, float);
           void display();
        private:
           float pay;
    };
    
    Graduate::Graduate(intn, string nam, float s, float p):Student(n, nam, s), pay(p) {}
    voidGraduate::display()
    {
        Student::display();
        cout << "pay = " <<pay << endl;
    }
    
    int main()
    {
        Student stud1(1001, "Li", 87.5);
        Graduate grad1(2001, "Wang", 98.5,5635.4);
    
        Student *pt = &stud1;
        pt->display();
    
        pt = &grad1;
        pt->display();
    
        return 0;
    }

    実際にptはgrad 1でベースクラスから継承された部分を指し,ベースクラスオブジェクトへのポインタにより派生クラスのベースクラスメンバーのみにアクセスでき,派生クラスが増加したメンバーにアクセスできない.
    次の章で解決する問題:ベースクラスポインタを使用してベースクラスと派生クラスオブジェクトのメンバーを呼び出す.
    5.8継承と組合せ
    2つの名詞に分けられる:サブオブジェクト、サブクラスオブジェクトが1つのクラスで別のクラスのオブジェクトをデータメンバーとする現象を、クラスの組合せと呼ぶ.
    クラスの継承:「はい」の関係、縦のクラスの組合せ:「あり」の関係、横の