OOP 11クラスの継承
派生と継承
派生とは、既存のクラスから新しいクラスを作成するプロセスを指す.新しいクラスは派生クラスまたはサブクラスと呼ばれ、既存のクラスはベースクラス、親クラスまたはスーパークラスと呼ばれている.
継承とは、子が親を保持するメンバーのプロパティを指す.
継承は、「自動所有」を意味する.すなわち、子クラスでは、親クラスで定義された属性およびアクションを再定義する必要はなく、親クラスの属性および動作を自動的に、暗黙的に所有することを意味する.
継承は伝達性を有し、派生クラスはベースクラスとしてもよい.
継承は汎化関係を体現し、コード多重を実現した.
派生クラスの定義
派生クラスの宣言:class; 派生クラスの定義フォーマットは、次のclass:{}; ベースクラスは定義されている必要があります.継承方式は公有、保護、私有継承の3種類があり、それぞれpublic、protected、privateで表され、デフォルトでは私有継承(structデフォルトでは公有継承)である.
class Base {
private:
int b_number;
public:
Base( ){}
Base(int i):b_number (i) { }
int get_number( )
{
return b_number;
}
void print( )
{
cout << b_number << endl;
}
};
class Derived : public Base {
private:
int d_number;
public:
Derived( int i, int j )
: Base(i), d_number(j) { };
void print( )
{
cout << get_number( )
<< " ";
cout << d_number
<< endl;
}
};
派生クラスメンバーの構成
派生クラスの生成は、ベースクラスメンバーの吸収、ベースクラスメンバーの改造、派生クラス新規メンバーの追加の3つのステップを経た.
吸収ベースクラスメンバーは、ベースクラスを継承するメンバーであるが、ベースクラスの構造関数、解析関数、コピー構造関数、および付与関数を継承しない.
改造ベースクラスメンバーは、派生クラス定義時の継承方式によって、派生クラスにおけるベースクラスメンバーのアクセス制御権限の変更を制御する2つの態様を含む.2つ目は、同じ名前のメンバーを定義するベースクラスメンバー(ただし、ベースクラスメンバーはまだ存在する)新しいメンバーを追加することは、派生クラスで新しいメンバーを定義し、新しい機能を拡張することである.また、独自の構造および構造関数も定義する.
派生クラスにおけるベースクラスメンバーのアクセス権
公有相続
クラスの継承方式が公有継承である場合、派生クラスではベースクラスの公有メンバーと保護メンバーが継承された後、それぞれ派生クラスの公有メンバーと保護メンバーとなり、派生クラスが新しく定義したメンバー関数は直接アクセスできるが、ベースクラスのプライベートメンバーに直接アクセスすることはできない.クラス外では、派生クラスのオブジェクトは、継承されたベースクラス共有メンバーにアクセスすることができる.
//rectangle.h
class Point
{
public:
void InitP(float xx=0, float yy=0)
{ X=xx; Y=yy; }
void Move(float xOff, float yOff)
{ X+=xOff; Y+=yOff; }
float GetX(){return X;}
float GetY(){return Y;}
private:
float X,Y;
};
class Rectangle: public Point
{
public:
void InitR(float x, float y,
float w,float h)
{ InitP(x,y); W=w;H=h; }
float GetH() {return H;}
float GetW() {return W;}
private:
float W,H;
};
保護継承
クラスの継承方式が保護継承である場合、派生クラスでは、ベースクラスの共有メンバーと保護メンバーが継承された後、すべて派生クラスの保護メンバーとなり、派生クラスが新しく定義したメンバー関数は直接アクセスできますが、ベースクラスのプライベートメンバーに直接アクセスすることはできません.クラス外では、派生クラスのオブジェクトは、継承されたすべてのベースクラスメンバーにアクセスできません.
保護メンバーは両面性を有し、外部には見えず、派生クラスにはが見える.
オブジェクト向けプログラミングでは、ベースクラスのいくつかの関数機能を拡張および改造するには、保護継承方式を採用し、派生クラスで同名のメンバー関数を定義して//rectangle.h
class Point
{
public:
void InitP(float xx=0, float yy=0)
{ X=xx; Y=yy; }
void Move(float xOff, float yOff)
{ X+=xOff; Y+=yOff; }
float GetX(){return X;}
float GetY(){return Y;}
private:
float X,Y;
};
class Rectangle: protected Point
{
public:
void InitR(float x, float y,
float w,float h)
{ InitP(x,y); W=w;H=h; }
void Move(float xOff, float yOff)
{ Point::Move(xOff,yOff); }
float GetX() {return Point::GetX();}
float GetY() {return Point::GetY();}
float GetH() {return H;}
float GetW() {return W;}
private:
float W,H;
};
を実現することができる.
プライベート継承
クラスの継承方式がプライベート継承である場合、派生クラスでは、ベースクラスの共有メンバーと保護メンバーが継承された後、すべて派生クラスのプライベートメンバーとなり、派生クラスが新しく定義したメンバー関数は直接アクセスできますが、ベースクラスのプライベートメンバーに直接アクセスすることはできません.クラス外では、派生クラスのオブジェクトは、継承されたすべてのベースクラスメンバーにアクセスできません.
インタフェース継承と実装継承
上から見ることができます:クラスの継承方式が公有継承である場合、派生クラスはベースクラスのインタフェースを継承する、ベースクラスと同じインタフェースを有し、通常はインタフェース継承と呼ぶ.
クラスの継承方式が保護またはプライベート継承である場合、派生クラスがベースクラスを継承する部分はそのインタフェースの一部にはならない、すなわち派生クラスはベースクラスのインタフェースを継承するのではなく、実装においてベースクラスから継承されたメンバーを用いるため、通常は継承実装実装実装実装実装実装実装継承と呼ばれる.ベースクラスから個別に継承するメンバーは、派生クラスで元のアクセス制御権限class Base
{
public:
size_t size() const
{ return n; }
protected:
size_t n;
};
class Derived : private Base
{
public:
using Base::size;
protected:
using Base::n;
// ...
};
をusing宣言で復元することができる.
派生クラスDerivedにusing宣言を追加し、sizeメンバーがクラス外にアクセスできるようにし、nがDerivedから派生するクラスにアクセスできるようにする注意:派生クラスではベースクラスのプライベートメンバーに対してusing宣言を使用できない.
ベースクラスのプライベートメンバーに関する注意
は、共有継承、保護継承、またはプライベート継承にかかわらず、ベースクラスのプライベートメンバーは派生クラスに直接アクセスできません.C++の処理は、privateタイプとしてメンバーが宣言されている以上、本クラスのオブジェクトでも派生クラスのオブジェクトでも、開発者が外部から直接アクセスすることを望んでいないことを示しているため、主にデータパッケージを考慮している.
派生クラスの構造関数
派生クラスのオブジェクトはベースクラスのすべてのデータメンバーを自動的に持つため、派生クラスの構造関数を定義する際に派生クラスのデータメンバーを初期化するほか、ベースクラスのデータメンバーを初期化する必要がある.派生クラスにオブジェクトメンバーがある場合、オブジェクトメンバーも初期化する.
派生クラス構築関数の定義フォーマットは以下の通りである[:]()[:(),()]{};//rectangle.h
class Point
{
public:
void Point(float xx=0, float yy=0)
{ X=xx; Y=yy; }
void Move(float xOff, float yOff)
{ X+=xOff; Y+=yOff; }
float GetX(){return X;}
float GetY(){return Y;}
private:
float X,Y;
};
class Rectangle: public Point
{
public:
Rectangle(float x, float y, float w,
float h)
:Point(x, y) { W=w; H=h; }
void Move(float xOff, float yOff)
{ Point::Move(xOff,yOff); }
float GetX() {return Point::GetX();}
float GetY() {return Point::GetY();}
float GetH() {return H;}
float GetW() {return W;}
private:
float W,H;
};
派生クラス構築関数の注意
ベースクラスから継承するメンバーの初期化、派生クラス構築関数は、関数内で直接値を付与する方式ではなく、初期化リストから直接ベースクラスの適切な構築関数を呼び出すことによって間接的に初期化することが望ましい.初期化リストで直接初期化またはベースクラスの他のメンバー関数を呼び出すことは絶対に許されない.ベースクラスにコンストラクション関数が明示的に定義されていない場合、派生クラスはコンストラクション関数を明示的に定義する必要がなく、またはコンストラクション関数を明示的に定義することができるが、初期化リストはベースクラスコンストラクション関数の呼び出しを省略することができる.
ベースクラスでコンストラクション関数を明示的に定義する場合、派生クラスもコンストラクション関数を明示的に定義し、初期化リストによって直接ベースクラスの適切なコンストラクション関数を呼び出すことによってベースクラスから継承されたメンバーを初期化する必要がある.
派生クラスの構築順序:
派生クラスコンストラクタの実行順序は、①ベースクラスコンストラクタを呼び出し、ベースクラスから継承するデータメンバーを初期化する②派生クラスに新規オブジェクトメンバーが追加された場合、その属するクラスのコンストラクタを新規オブジェクトメンバーの宣言順に順次呼び出し、新規オブジェクトメンバーを初期化する③派生クラスコンストラクタを実行する関数体を初期化し、派生クラスに新規の他のデータメンバーを初期化する.
派生クラスの構造関数
派生クラス構造関数の定義形式は以下の通りである~(){}派生クラスの構造関数は、ベースクラスの構造関数派生クラス構造関数実行順序を自動的に呼び出す:①派生クラス構造関数を実行する関数体②宣言順序の逆順序で各新規の対象メンバー③呼び出しベースクラスの構造関数を順次解析する.