継承は、「引き継ぐ」と言う意味があります。
クラスの継承とは、その名の通り
クラスの機能(メンバ変数、メンバ関数など)を
引き継いで新しいクラスを作ると言う意味です。
継承はインヘリタンスと呼ぶ場合もあります。
一般的な使い方としては、基本的な機能を有する基本クラスを作っておき、
派生クラスでは、その基本クラスを継承して、基本的な機能を取り込み、
それに加えて、新しく独自の機能を盛り込む。と言った感じです。
引継ぎ元となるクラスを
基本クラスや基底クラス、ベースクラス、
親クラス、スーパークラスなどと呼び、
そのクラスを引き継いで新しく作るクラスを
派生クラスやサブクラス、子クラスなどと呼びます。
派生クラスの定義では、基本クラスで定義されていたメンバ変数や
メンバ関数などは、一切書く必要はなく、
追加機能の実装だけに専念できるため、
非常にスマートな記述が可能です。
クラスの継承サンプルコード
#include <iostream>
using namespace std;
/********** 基本クラス **********/
class kihon {
private:
int data1;
public:
kihon() { data1 = 0; }
kihon(int d) { data1 = d; }
void set_data1(int d) { data1 = d; }
void show_data1();
};
void kihon::show_data1() {
cout << "data1は" << data1 << "です" << '\n';
}
/********** 派生クラス **********/
class hasei : public kihon { // 基本クラスkihonを継承して
private: // 派生クラスhaseiを定義
int data2;
public:
hasei();
hasei(int d1, int d2);
void set_data2(int d1, int d2);
void show_data2();
};
hasei::hasei() {
kihon(); // 基本クラスのコンストラクタを使用
data2 = 0;
}
hasei::hasei(int d1, int d2) {
set_data1(d1); // 基本クラスのメンバ関数を使用
data2 = d2;
}
void hasei::set_data2(int d1, int d2) {
set_data1(d1); // 基本クラスのメンバ関数を使用
data2 = d2;
}
void hasei::show_data2() {
show_data1(); // 基本クラスのメンバ関数を使用
cout << "data2は" << data2 << "です" << '\n';
}
/********** main関数 **********/
void main(void) {
/***** 基本クラスの動作 *****/
kihon a(10);
a.show_data1();
/***** 派生クラスの動作 *****/
hasei b(50, 100);
b.set_data2(100, 200);
b.show_data2();
b.set_data1(30); // 基本クラスのメンバ関数を使用
b.show_data1(); // 基本クラスのメンバ関数を使用
}
このプログラム例では、
基本クラスkihonとその派生クラスhaseiの簡単な継承例です。
派生クラスを宣言する時、
基本クラスの左側にアクセス指定子がついていますが、
コレについては、コチラで説明しています。
基本クラスkihonのメンバ変数data1は、継承によって、
派生クラスhasei内にも存在する事になります。
同じく、基本クラスのメンバ関数は全て継承されるので、
派生クラスのメンバ関数として使う事ができます。
基本クラスkihonのメンバ変数data1は、privateメンバ変数なので、
派生クラスからは直接アクセスすることはできません。
基本クラスのメンバ変数を派生クラスからアクセス可能にしたい場合には、
メンバ変数にアクセス指定子protectedかpublicを指定しておく必要があります。
派生クラスのコンストラクタ記述
上記の例では、
派生クラスのメンバ関数内から基本クラスの関数を利用していますが、
派生クラスのコンストラクタ内容は、
基本クラスのコンストラクタ内容を含む事が多いので、
特殊な書き方によって、
派生クラスのコンストラクタ内に、
基本クラスのコンストラクタの処理をさせるようにする事ができます。
上の例で示したプログラム例の
派生クラスhaseiのコンストラクタ内の記述例を書いてみます。
特殊な書き方をしない場合
hasei::hasei() {
kihon();
data2 = 0;
}
特殊な書き方をする場合
hasei::hasei() : kihon() {
data2 = 0;
}
hasei::hasei(int d1, int d2) : kihon(d1) {
data2 = d2;
}c
関数定義の右側にコロン(:)で区切って、
基本クラスのコンストラクタ呼び出し記述を書きます。
動作順序としては、
まず、派生クラスのコンストラクタが呼ばれたら、
この時の引数を使って基本クラスのコンストラクタを呼び出してから、
派生クラスのコンストラクタ内容が実行されます。
基本、派生クラス間の関数オーバーロード
基本クラスと派生クラスに、
同じ名前の関数が存在する事は許されています。
通常の関数のオーバーロードでは、
引数の型や引数の数によって、どの関数か判別するため、
同じ引数の型、同じ引数の数の関数はオーバーロード不可能でした。
しかし、基本、派生クラス間の関数オーバーロードの場合は、
同じ引数の型、同じ引数の数の関数を作っても問題にはなりません。
もちろん、異なる引数の型、異なる引数の数の関数でも問題ありません。
それは、呼び出す時にスコープ解決演算子を使う事で判別可能なためです。
動作例
基本クラスkihonと派生クラスhaseiに
show関数がオーバーロードされているとします。
この時、引数はどちらもナシとします。
kihon a;
hasei b;
a.show(); // 基本クラスのshow()が呼ばれる
b.show(); // 派生クラスのshow()が呼ばれる
b.kihon::show(); // 基本クラスのshow()が呼ばれる
基本クラスのインスタンスからは、
基本クラスのshow関数しか呼べません。
しかし、派生クラスのインスタンスからは、
スコープ解決演算子を使う事で
どちらのshow関数も呼ぶことができます。