こんにちは、バンバンです。今日は相続について話しましょう。
オブジェクト指向の 3 つの主要な機能: カプセル化、継承、ポリモーフィズム。
目次
1.2.3 継承された基本クラスのメンバーのアクセス方法の変更
1. 継承の概念と定義
1.1 継承の概念
1.2 継承の定義
1.2.1 フォーマットの定義
person は親クラスであり、基本クラスとも呼ばれます。Student はサブクラスであり、派生クラスとも呼ばれます。
1.2.2 継承とアクセス修飾子
1.2.3 継承された基本クラスのメンバーのアクセス方法の変更
クラスメンバー/継承
|
パブリック継承
|
保護された継承 | 私的継承 |
基本クラスのパブリックメンバー
|
派生クラスのパブリック メンバー
|
派生クラスの保護されたメンバー
|
派生クラスのプライベート メンバー
|
基本クラスの保護されたメンバー
|
派生クラスの保護されたメンバー
|
派生クラスの保護されたメンバー
|
派生クラスのプライベート メンバー
|
基本クラスのプライベートメンバー
|
派生クラスでは表示されません
|
派生クラスでは表示されません
|
派生クラスでは表示されません
|
オフトピック: 基本クラスのプライベート メンバーは、継承メソッドに関係なく非表示であることがわかります。実際、これは C++ ボスの落とし穴です。ボスはすべての状況を設計に含めようとしますが、実際の Inほとんどの場合、ほとんどのプロジェクトはパブリックを使用し、プライベートはほとんど使用しませんが、言語の発展が進んでいるために、上司が落とし穴を避けるために言語をロールバックすることはほとんど不可能です (Python3 が Python2 と互換性がないことを除く)。この穴を埋める方法を考えてください。それが穴であることはわかっていますが、私たちはまだ学ばなければなりません。
要約:
- 基本クラスのプライベート メンバーは、継承方法に関係なく、派生クラスには表示されません。ここでの 非可視性とは、基本クラスのプライベート メンバーは引き続き派生クラス オブジェクトに継承されますが、派生クラス オブジェクトはクラスの内外に関係なくアクセスできないように文法的に制限されていることを意味します 。
- 基本クラスのプライベート メンバーには、派生クラスではアクセスできません。基本クラスのメンバーにクラス外から直接アクセスしたくないが、派生クラスではアクセスできるようにする必要がある場合、そのメンバーは保護済みとして定義されます。protected メンバー修飾子は継承によるものであることがわかります。
- 実際、上記の表を要約すると、基本クラスのプライベート メンバーがサブクラスでは表示されないことがわかります。サブクラス内の基本クラスの他のメンバーのアクセス メソッド == Min (基本クラスのメンバーのアクセス修飾子、継承メソッド) 、public > protected > private。
- キーワードclass を使用する場合、デフォルトの継承メソッドは private であり、struct を使用する場合、デフォルトの継承メソッドは public ですが、継承メソッドを明示的に記述することをお勧めします。
- 実際には、パブリック継承が一般に使用され、プロテクト/プライベート継承はめったに使用されません。また、プロテクト/プライベート継承メンバーは派生クラスでのみ使用できるため、継承の使用は推奨されません。。
トピック:
継承できないクラスを定義するにはどうすればよいですか?
答え:
C++98: 親コンストラクターのプライベート - サブクラスは表示されません。サブクラス オブジェクトはインスタンス化されており、コンストラクターを呼び出すことはできません。
C++11: 新しく追加されたキーワード Final (最終クラス)
2. 基底クラスと派生クラスのオブジェクト代入変換
- 派生クラスのオブジェクトは、基本クラスのオブジェクト/基本クラスのポインター/基本クラスの参照に割り当てることができます。ここにはスライスまたはカットと呼ばれる比喩的な用語があります。派生クラスの親クラス部分を切り取って過去に代入するということです。
- 基本クラスのオブジェクトを派生クラスのオブジェクトに割り当てることはできません。
- 基本クラスのポインタまたは参照は、キャストを通じて派生クラスのポインタまたは参照に代入できます。ただし、基本クラスのポインタが派生クラス オブジェクトを指している場合は安全でなければなりません。ここで、基本クラスがポリモーフィック型の場合、RTTI (Run-Time Type Information) の Dynamic_cast を使用して識別し、安全な変換を実行できます。
1. サブクラス オブジェクトは、親クラスのオブジェクト/ポインターおよび参照に直接割り当てることができます。
Student sobj ; // 1.子类对象可以赋值给父类对象/指针/引用 Person pobj = sobj ; Person* pp = &sobj; Person& rp = sobj;
2. 基本クラスのオブジェクトを派生クラスのオブジェクトに割り当てることはできません。
//2.基类对象不能赋值给派生类对象 sobj = pobj;//错误
3. 強制型変換により、基底クラスのポインタを派生クラスのポインタに代入できる
pp = &sobj Student* ps1 = (Student*)pp; // 这种情况转换时可以的。 ps1->_No = 10; pp = &pobj; Student* ps2 = (Student*)pp; // 这种情况转换时虽然可以,但是会存在越界访问的问题 ps2->_No = 10;
3. 継承の範囲
4. 派生クラスのデフォルトのメンバー関数
- コンストラクタ
サブクラス自体のメンバーはクラスと同じです。(組み込み型は処理しません -> ランダムな値、デフォルト値を与える、デフォルト値が処理します。カスタム型はその処理を呼び出します)
親クラスのメンバーを継承するには、親クラスのコンストラクターを呼び出す必要があります。基本クラスにデフォルトのコンストラクターがない場合、サブクラス コンストラクターの初期化リスト フェーズ中に呼び出しを明示的に行う必要があります。
サブクラスを構築する前に、まず親クラスを構築します。
サブクラスは、初期化リストで親クラスのメンバー変数を直接初期化することはできません。また、匿名オブジェクトは初期化リストで初期化されます。
class Person
{
public:
Person(const char* name)
: _name(name)
{
cout << "Person()" << endl;
}
protected:
string _name;
}
class Student:public Person
{
public:
Student(const char* name, int num)
:Person(name) //匿名对象在初始化列表初始
, _num(num)
{
cout << "Student()" << endl;
}
protected:
int _num;
}
- デストラクター
同上
これは、親クラスのデストラクターを明示的に呼び出すことなく、自動的に呼び出されます。最初に子クラスが破棄され、次に親クラスが破棄されます。
- コピーコンストラクター
クラスやオブジェクトなどの独自のメンバー。(組み込み型はコピー -> 浅いコピーを参照し、カスタム型はそのコピー構築を呼び出します)
継承された親クラスのメンバーは、親クラスのコピー コンストラクターを呼び出して初期化する必要があります。
- 演算子=
同上
親クラスのコピーを完了するには、operator= は親クラスの Operator= を呼び出す必要があります。
5. 継承とフレンドと静的メンバー
5.1 相続と友人
5.2 継承と静的メンバー
6. 継承モデル
6.1 単一継承
6.2 多重継承
トピック:
多重継承: (オブジェクト分散) 最初の継承が前にあり、後の継承が後ろにあります。
スライス:
p1 と p3 が実際には同じ場所を指していることがわかります。
メンバ変数はスタック上に置かれるローカル変数で、input要素がスタックの先頭に置かれ、アドレスが徐々に増えていきますが、b2は後から引き継がれるので、b1よりアドレスが大きい、つまりp2の方が上位になりますp1よりも。
6.3 ダイヤモンドの継承
6.3.1 ダイヤモンドの仮想継承
6.3.2 ひし形仮想継承の原理
ここでは、研究のために簡略化されたダイヤモンド継承モデルを示します。
class A
{
public:
int _a;
};
//class B : public A
class B : virtual public A
{
public:
int _b;
};
//class C : public A
class C : virtual public A
{
public:
int _c;
};
class D : public B, public C
{
public:
int _d;
};
int main()
{
D d;
d.B::_a = 1;
d.C::_a = 2;
d._a = 0;
d._b = 3;
d._c = 4;
d._d = 5;
return 0;
}
- まず、仮想継承 virtual を使用しない場合、メモリ状況は次のようになります。
格納状況は、上で示したダイヤモンド型の継承オブジェクト モデルに準拠します。
- 仮想継承 virtual を使用すると、メモリの状況は次のようになります。
D オブジェクトでは、A がオブジェクト構成の最下位に配置されていることがわかります。この A は、B と C に同時に属します (共通 A)。B と C が A を格納するアドレスは 2 つあります。この 2 つのアドレスは、 (つまり、仮想実表ポインタ) はテーブル (仮想実表) を指し、オフセットは仮想実表に格納され、オフセットを通じて共通の A が見つかります。
7. 継承の概要
3.継承と合成
- パブリック継承はis-a関係です。つまり、すべての派生クラス オブジェクトは基本クラス オブジェクトです。例: 人々 - 学生。
- 構成はhas-a の関係です。B が A を構成すると仮定すると、各 B オブジェクトの中に A オブジェクトが存在します。例: タイヤ - 車。
- クラスの継承よりもオブジェクトの合成を優先します。
- 継承を使用すると、基本クラスの実装に関して派生クラスの実装を定義できます。派生クラスの生成によるこの種の再利用は、多くの場合、ホワイトボックス再利用と呼ばれます。「ホワイト ボックス」という用語は可視性と関連しています。継承では、基本クラスの内部詳細がサブクラスから見えます。継承により基底クラスのカプセル化はある程度破壊され、基底クラスの変更は派生クラスに大きな影響を与えます。派生クラスと基底クラスとの依存関係は非常に強く、結合度は高い。
- オブジェクトの合成は、再利用のためのクラス継承の代替手段です。オブジェクトを組み立てたり組み合わせたりすることで、より複雑な新しい機能を得ることができます。オブジェクトを合成するには、合成されるオブジェクトに明確に定義されたインターフェイスが必要です。このスタイルの再利用は、オブジェクトの内部の詳細が目に見えないため、ブラックボックス再利用と呼ばれます。オブジェクトは「ブラック ボックス」としてのみ表示されます。複合クラス間には強い依存関係はなく、結合度は低い。オブジェクトの構成を優先すると、各クラスをカプセル化した状態に保つことができます。
- 実際には、できるだけ多くの組み合わせを使用してください。組み合わせの結合度が低く、コードの保守性が良い。ただし、継承も便利です。一部の関係は継承に適しているため、継承を使用します。また、多態性を実現するには、継承も必要です。クラス間の関係は、継承を使用することも、組み合わせを使用することも、組み合わせを使用することもできます。