この記事では、C ++の研究ノートで、第8版、「C ++入門」第5版、「完全なコード」バージョン2「自分が1時間で日をC ++教えるサムズ」を参照してください。
継承が多重化され、オブジェクトの抽象化の異なるレベルが同じ特性を再利用することができます。別のクラス(クラスイル)例外は、クラス(派生クラス)を説明するため、典型的に継承されています。継承の目的は、より多くのコードを書く合理方法「基底クラスは、共通の要素派生クラス二つ以上として定義することができます」。
相続の基礎1。
承継の基本を説明するためにこのセクションでは、パブリック継承。
日常生活の中での継承の例:
基底クラス | 派生クラス |
---|---|
魚(魚) | 金魚(金魚)、コイ(鯉)が、マグロ(マグロ、マグロは魚です) |
哺乳動物(哺乳類) | ヒト(人)は、象(ゾウ)、ライオン(ライオン)、カモノハシ(カモノハシは、カモノハシは哺乳類です) |
鳥(鳥) | クロウ(クロウ)、オウム(オウム)、オーストリッチ(ダチョウ)、カモノハシは(カモノハシ、カモノハシは鳥です) |
Shape(形状) | サークル(円)、ポリゴン(多角形、多角形) |
ポリゴン(多角形) | 三角形(三角)、オクタゴン(八角形、八角形の形状が多角形であり、多角形) |
1.1継承と派生
(例えば、マグロなど)、派生クラスを導出する(魚など)の基本クラスは、派生クラスは、ベースクラスを継承します。公共の継承は、派生クラスが基底クラスである、例えば、我々が言うことができ、マグロの一種です。
文学を読むこと継承、記述した場合(から継承)「から継承されたの...来る」と「から派生...来る」と同じ用語の意味(から派生)。同様に、ベース・クラス(基底クラス)も、スーパークラス(スーパークラス)と呼ばれ、基底クラスから派生したクラスは、派生クラス(派生クラス)と呼ばれ、さらにサブクラス(サブクラス)と呼ばれます。
コンストラクタ1.2をカバー継承
クラスの初期化は、その直接の基底クラスは、同じ理由で、コンストラクタクラスは、唯一の直接基底クラスを継承しています。
派生クラスは、宣言文を使用して、基本クラスのコンストラクタ直接法を継承し、次のとおりです。
class Base
{
public:
Base() {}; // 1. 默认、拷贝、移动构造函数不能被继承和覆盖
Base(int a) {}; // 2. 被派生类中的构造函数覆盖
Base(int a, int b) {}; // 3. 被派生类中的构造函数继承
Base(int a, string b) {}; // 3. 被派生类中的构造函数继承
};
Class Derived: public Base
{
public:
using Base::Base; // 继承基类中的构造函数
Derived(int a) {}; // 覆盖基类中的构造函数
};
一般的に、ステートメントを使用すると、ちょうど現在のスコープで見える名前を作りました。そして、コンパイラがコードを生成するようになります宣言を使用して、コンストラクタとして使用した場合。基底クラスの各コンストラクタは、コンパイラは、派生クラスのコンストラクタに完全に相当するパラメータのリストを生成します。派生クラスのコンストラクタ基底クラスのコンストラクタパラメータテーブル場合、派生クラスのコンストラクタは、基本クラスのコンストラクタをカバーに相当し、この場合は、基本クラスのコンストラクタによって覆われていることができない。しかし、2つの例外は、最初は、あります継承された2番目:デフォルトでは、コピー、移動のコンストラクタは継承されません。これらの規則は、コンパイラによって生成された派生クラスの関数の形で、次のコードによって構成された実施例によれば:
Class Derived: public Base
{
public:
Derived(int a, int b) : Base(a, b) {};
Derived(int a, string b) : Base(a, b) {};
};
1.3派生クラスは、基本クラスのコンストラクタを呼び出します
派生クラスは基底クラスのコンストラクタには3つの形式があります呼び出します。
- 基底クラスは、デフォルトコンストラクタを持っている場合は、派生クラスのコンストラクタ暗黙のデフォルトコンストラクタは、呼び出し元のコードを記述することなく、コンパイラによって実装される基本クラスを呼び出します。
- クラスはデフォルトコンストラクタベース、すなわち、基底クラスのコンストラクタのオーバーロードを持っていない場合は、派生クラスのコンストラクタは、明示的な呼び出しに属しているリストを、初期化することにより、基本クラスのコンストラクタを呼び出します。このアプローチは必要であり、そうでない場合、コンパイラは、基本クラスのデフォルトコンストラクタを呼び出すようにしようとすると、基底クラスにはデフォルトコンストラクタを持っていない、コンパイラは間違って行くだろう。
- 派生クラスのコンストラクタは、
::Base()
ディスプレイの形態は、基本クラスのコンストラクタを呼び出します。基本クラスは、様々な通常の関数によって呼び出されると、フォーム派生クラスは、通常の関数の基本クラスを呼び出しますBase::Function()
(クラス名を指定する必要があります)。この方法及び第二の態様の欠如で使用されるこの方法と本質的に同じ機能を実現するには2つの方法ながら、しかし、場合にのみ、コンパイラが間違って行くでしょう。このアプローチは意味をなすようには見えません。
基底クラスコンストラクタのオーバーロードが含まれている場合、それはインスタンス化の時点での引数を提供することが必要である、派生オブジェクトが作成され、リストには、派生クラスのコンストラクターによって適切な基底クラスのコンストラクタを初期化し、呼び出すために使用されてもよいです。
class Base
{
public:
Base(int a) { m = a };
private:
int m;
};
Class Derived: public Base
{
public:
Derived(): Base(25) {}; // 基类构造函数被调用一次,最终 Base::m 值为 25
Derived(): Base(25) { ::Base(36) }; // 基类构造函数被调用两次,最终 Base::m 值为 25
Derived() { ::Base(36) }; // 编译器试图调用基类默认构造函数 Base::Base(),编译出错
};
1.4デストラクタシーケンシャル施工順序
マグロ魚から継承する構成のため、マグロオブジェクトが作成された第1の部分構成マグロフィッシュにおける2再構成可能なマグロマグロ部分に.. 次いで、インスタンス化部とマグロ部、メンバプロパティの最初のインスタンス、およびコンストラクタを呼び出します。逆の順序や構成のデストラクタシーケンス。次のように例の手順は以下のとおりです。
#include <iostream>
using namespace std;
class FishDummyMember
{
public:
FishDummyMember() { cout << "FishDummyMember constructor" << endl; }
~FishDummyMember() { cout << "FishDummyMember destructor" << endl; }
};
class FishPrivateMember
{
public:
FishPrivateMember() { cout << "FishPrivateMember constructor" << endl; }
~FishPrivateMember() { cout << "FishPrivateMember destructor" << endl; }
};
class Fish
{
protected:
FishDummyMember dummy;
private:
FishPrivateMember dummy2;
public:
Fish() { cout << "Fish constructor" << endl; }
~Fish() { cout << "Fish destructor" << endl; }
};
class TunaDummyMember
{
public:
TunaDummyMember() { cout << "TunaDummyMember constructor" << endl; }
~TunaDummyMember() { cout << "TunaDummyMember destructor" << endl; }
};
class Tuna: public Fish
{
private:
TunaDummyMember dummy;
public:
Tuna() { cout << "Tuna constructor" << endl; }
~Tuna() { cout << "Tuna destructor" << endl; }
};
int main()
{
Tuna tuna;
}
FishDummyMemberとTunaDummyMember:メンバ変数をインスタンス化し、破壊された方法を理解するには、2つの無用の空のクラスを定義します。次のようにプログラムの出力は次のとおりです(//代わりのコンテンツを印刷、文です)
FishDummyMember constructor // 基类数据成员实例化
FishPrivateMember constructor // 基类数据成员实例化
Fish constructor // 基类构造函数
TunaDummyMember constructor // 派生类数据成员实例化
Tuna constructor // 派生类构造函数
Tuna destructor // 派生类析构函数
TunaDummyMember destructor // 派生类数据成员销毁
Fish destructor // 基类析构函数
FishPrivateMember destructor // 基类数据成员销毁
FishDummyMember destructor // 基类数据成员销毁
派生クラスのオブジェクトの構成は、基本クラスのプライベートデータメンバがインスタンス化されますが、派生クラスは、基本クラスのprivateメンバにアクセスする権限を持っていないことに注意してください。参考セクション3.1。
1.5カバーとベースクラスのメソッドを隠します
#include <iostream>
using namespace std;
class Fish
{
private:
bool isFreshWaterFish;
public:
// Fish constructor
Fish(bool IsFreshWater) : isFreshWaterFish(IsFreshWater){}
// using Fish::Swim; // 4.2 基类中所有 Swim() 方法不作隐藏
void Swim() // 1.1 此方法被派生类中的方法覆盖
{
if (isFreshWaterFish)
cout << "[A] Fish swims in lake" << endl;
else
cout << "[A] Fish swims in sea" << endl;
}
void Swim(bool freshWater) // 1.3 此方法被派生类中的方法隐藏
{
if (freshWater)
cout << "[B] Fish swims in lake" << endl;
else
cout << "[B] Fish swims in sea" << endl;
}
void Fly()
{
cout << "Joke? A fish can fly? << endl;
}
};
class Tuna: public Fish
{
public:
Tuna(): Fish(false) {}
void Swim() // 1.2 覆盖派生类中的方法
{
cout << "Tuna swims real fast" << endl;
}
};
class Carp: public Fish
{
public:
Carp(): Fish(true) {}
void Swim() // 1.2 覆盖基类中的方法
{
cout << "Carp swims real slow" << endl;
Fish::Swim(); // 3.2 在派生类中调用基类方法(继承得到)
Fish::Fly(); // 5.2 在派生类中调用基类方法(继承得到)
}
/*
void Swim(bool freshWater) // 4.3 覆盖基类中 Swim(bool) 方法
{
Fish::Swim(freshWater);
}
*/
};
int main()
{
Carp carp;
Tuna tuna;
carp.Swim(); // 2.1 调用派生类中的覆盖方法
tuna.Swim(); // 2.2 调用派生类中的覆盖方法
tuna.Fish::Swim(); // 3.1 调用基类中被覆盖的方法
tuna.Fish::Swim(false); // 4.1 调用基类中被隐藏的方法
tuna.Fly(); // 5.1 调用基类中的其他方法(继承得到)
return 0;
}
カバーと、リファレンスノート1.1 1.2 1.3を隠す方法。
カバーの方法は、基準注釈2.1 2.2、派生クラスを呼び出します。
基本クラスのメソッドは、パラメータコメント3.1 3.2を覆われている呼び出します。
基底クラス隠されたメソッド、パラメータ、注釈4.1 4.2 4.3を呼び出します。
他の方法は、基本クラス、パラメータの注釈5.1 5.2を呼び出します。
継承とクラス2.アクセス
また、3つの主要なアクセス修飾子として知られている公共(パブリック)、保護(保護)とプライベート(プライベート):3件のアクセスがあります。アクセス修飾子は、どちらの場合に表示されます:1は、クラスのメンバーへのアクセスで、クラスがメンバーとプライベートメンバーを保護し、公共のメンバーが、継承は、クラスの継承は、private継承と承継3の公共の継承、保護を持っています。
これら2例の組み合わせへのアクセスは、コンパイラは、最低のアクセス権を持つ派生クラス継承し、基本クラスのメンバーを確保するために、最も厳格なポリシーを使用する場合。たとえば、private継承の基底クラスのパブリックメンバーが検出され、それが派生クラスのプライベートメンバーになると、基本クラスの保護されたメンバーは、公共の継承は、派生クラスのprotectedメンバになっている場合に遭遇、派生の基底クラスのプライベートメンバークラスは表示されません。
派生クラスの基本クラスのプライベートメンバーが表示されていないが、派生クラスのオブジェクトで、基本クラスの実際の情報プライベートメンバーが含まれているが、それはそれにアクセスする権限を持っていないことに注意してください。参考セクション3.1。
2.1クラスのメンバへのアクセス
クラスのメンバーは、アクセスの3種類があります。
公共:公共のメンバーは、クラス外部からのアクセスを許可します。クラスの実施形態は、派生クラスの内部を介してオブジェクト・クラス、派生クラスのオブジェクトへのアクセス、及びアクセスによって外部アクセスにアクセスすることを含みます。
保護:保護されたクラスのメンバーが内側せ、派生クラスのフレンドクラスの内部アクセスの内側、外側の継承階層へのアクセスをブロックします。
プライベート:プライベートメンバーは、クラス内でアクセスすることができます。
内部クラスは、クラス宣言を含み、クラスコードの外部呼び出し、および現在のクラス宣言と他のクラスの実装コードを含む部分を実装します。
2.2公共の継承
公共の継承は、派生クラスのメンバとして基本クラスのパブリック及び保護されたメンバーのメンバーによって特徴付けられ、それらは元の状態を維持しています。派生クラスの基本クラスのプライベートメンバーの派生クラスで基本クラスのパブリックメンバーはまた、公共のメンバーで、派生クラスで基本クラスの保護されたメンバーも保護されてメンバーが表示されません。
の「A」のための公共の継承関係( - aがあります)。-ようマグロなどの基本クラスを表す派生クラス(派生クラス)は、魚(基本クラス)です。
2.3民間の継承
プライベート継承は、派生クラスのプライベートメンバーとなっている基底クラスのpublicおよびprotectedメンバーのメンバーによって特徴付けられます。基底クラスのプライベートメンバーは、派生クラスが表示されていない、基底クラスにプライベート残ります。
唯一プライベート継承派生クラスは、ベースクラスのプロパティとメソッドを使用し、従って「部分」を表すことができるように、関係(-Aを有します)。そのようなエンジン(基本クラス)などの派生クラスの一部を表す有するベースクラスは、車両(派生クラス)の一部です。
2.4保護された継承
保護継承は公衆のメンバーによって特徴付けられ、基本クラスの保護されたメンバーは、派生クラスの保護されたメンバーとなっています。基底クラスのプライベートメンバーは、派生クラスが表示されていない、基底クラスにプライベート残ります。
そして、同様のprivate継承、継承の保護があり、関係を述べました。異なる時間には、基本クラスのパブリック及び保護されたメンバーは、派生クラスの保護されたメンバーになる、サブクラスは、派生クラスと、派生クラスによってアクセスすることができます。
2.5概要
以下の表に、ヘッダ部分は、ベースクラスの3人のメンバーを表し、テーブルの本体は、派生クラスに対応するアクセス基本クラスのメンバーを別の継承を表します。第二列の第4行は、プライベート継承において発現列で形成するために、基本クラスのパブリックメンバは、派生クラスのプライベートメンバーになるであろう。
基底クラスのメンバー | 公共のメンバー | 保護されたメンバー | プライベートメンバ |
---|---|---|---|
そこ継承 | 公共のメンバー | 保護されたメンバー | インビジブル |
保護された継承 | 保護されたメンバー | 保護されたメンバー | インビジブル |
プライベート継承 | プライベートメンバ | プライベートメンバ | インビジブル |
3.ベースクラスの割り当てオブジェクトの派生クラスのオブジェクト間の関係
派生オブジェクトの基本クラスとの間の3.1の関係
#include <iostream>
using namespace std;
class Base
{
private:
int x = 1;
int y = 2;
const static int z = 3;
};
class Derived : public Base
{
private:
int u = 11;
int v = 22;
const static int w = 33;
};
int main()
{
Base base;
Derived derived;
cout << "sizeof(Base) = " << sizeof(Base) << endl;
cout << "sizeof(Derived) = " << sizeof(Derived) << endl;
return 0;
}
プログラムの出力は次のようになります。
sizeof(Base) = 8
sizeof(Derived) = 16
静的メンバクラスではない()はsizeof(クラス名はsizeof(オブジェクト名)に等しい)はsizeofに含まれていない特定のオブジェクトクラスに属し、8こうしてのsizeof(ベース)値を、クラスに属します。派生派生クラスのために、基本クラスのデータメンバーそれらのsizeof演算結果は、サイズをフットプリントに加えて派生クラス・データ・メンバーは、16のように値を空間を占めます。
なお、派生クラスのオブジェクトは、基本クラスのプライベートデータメンバーを含む基本クラスのデータメンバを、含まれていますが、派生クラスは、基本クラスのプライベートデータメンバにアクセスする権限を持っていないメモリ空間、コンパイラが構文に対応していません。
、gdbデバッガを使用して、基本クラスのオブジェクトや派生オブジェクトの値をプリントアウトし、以下の情報を提供します:
(gdb) p base
$1 = {x = 1, y = 2, static z = 3}
(gdb) p derived
$2 = {<Base> = {x = 1, y = 2, static z = 3}, u = 11, v = 22, static w = 33}
3.2削除の問題
コピー基本クラスオブジェクトへの派生オブジェクトは、次の2つの状況があります。
最初のベースクラス代入演算子にオブジェクトによって導出オブジェクトをコピー
Derived objDerived;
Base objectBase = objDerived;
第二は:により通過ベースオブジェクトパラメータに派生オブジェクトをコピー
void UseBase(Base input);
...
Derived objDerived;
UseBase(objDerived); // copy of objDerived will be sliced and sent
両方の場合において、コンパイラは、ベース部ではなく、オブジェクト全体をコピーするよりも、オブジェクトを派生コピーされます。そのようなデータが誤って切断し、行動をもたらすこと(スライス)に塩基由来カットと呼ばれています。
除去の問題を回避するには、値でパラメータを渡すことはありませんが、基本クラスまたはconst参照へのポインタとして渡す必要があります。リファレンス「C ++多型」セクション注1。
3.3割り当て関係
次の三つの関係の根本的な原因は、3.1節で語られてきました。
派生クラスのオブジェクトではなく、逆もまた同様、基本クラスのオブジェクトに割り当てることができます。
派生クラスのオブジェクト・データ・メンバと基本クラス・オブジェクト・データ・メンバーよりもため。オブジェクトの基本クラスに割り当てられた派生オブジェクトは、すべてのオブジェクトの基本クラスは、データメンバの値を得ることができます。次に、派生クラスのオブジェクトに割り当てられた、基本クラスのオブジェクト、派生クラス・オブジェクト、データ・メンバーの一部は、適切な値を得ることができない、割り当ては失敗します。
派生クラスへのポインタではなく、その逆に、ベースクラスのポインタを割り当てることができます。
メモリブロックサイズに派生クラスのポインタが指す基底クラスポインタ点よりメモリブロックからです。ベースポインタは、基本クラスにメモリのサイズを取って、誘導されたオブジェクトを指すことができます。逆に、基本クラスのオブジェクトに対して派生クラスポインタが範囲外のメモリを引き起こすことがバインドされています。
派生クラスオブジェクトはなく、その逆に、ベースクラスを参照するように割り当てることができます。
派生クラスのオブジェクトは、基本クラスのオブジェクト・スペースよりも大きいからです。基準ベース、ベース・クラスに割り当てられた派生オブジェクトクラスは、派生クラスのオブジェクトの基部は、過剰部分を破棄することを意味します。ターンでは、明らかではありません。
次のように:
Base base;
Derived derived;
base = derived; // 正确
derived = base; // 错误
Base *pbase = &derived; // 正确
Derived *pderived = &base; // 错误
Base &rbase = derived; // 正确
Derived &rderived = base; // 错误
4.多重継承
前記派生クラスの複数の基底クラスを多重継承と呼ばれる継承します。カモノハシがあるような。カモノハシは哺乳類、鳥類、爬虫類の特徴があり、カモノハシは哺乳類、鳥類、爬虫類3つの基底クラスを継承することができます。コードの形式:
class Platypus: public Mammal, public Reptile, public Bird
{
// ... platypus members
};
5.継承を禁止します
C ++ 11コンパイラから起動すると、予選決勝をサポートしています。最後のクラスは基本クラスとして使用することはできないとして宣言され、したがって、継承を禁止しています。