← オブジェクト指向プログラミングPart4 ポリモーフィズム (前回)
##純粋仮想関数と抽象クラス
__純粋仮想関数__とは基底クラス内で宣言だけをして定義付けをしないメンバ関数のことです。純粋仮想関数がひとつでもあれば、そのクラスを__抽象クラス__と呼びます。
抽象クラスは定義してないメンバ関数(純粋仮想関数)を含むため、プログラム内で抽象クラスを呼び出してもコンパイルエラーとなります。抽象クラスを使うためには__必ず純粋仮想関数をオーバーライド__(派生クラスで同じ関数を宣言、定義すること)をして、__抽象クラスではなく派生クラスを呼び出して__使うようにしなければいけません。
###何のために抽象クラスを作るのか
- 基底クラスを呼び出せないようにする(必ず派生クラスを使うようにする)
- 派生クラスで必ず定義しなければいけない関数を指定する(必ずオーバーライドさせる)
このふたつが抽象クラスを作る大きな理由です。基底クラスと派生クラスの関係はPart3で解説しましたが、実際にひとつの基底クラスに対して複数の派生クラスに枝分かれしてプログラムを書いていけば、基底クラス自体を呼び出して使うメリットがなくなっていきます。
そこで基底クラスを単体で使うことはなくして、__派生クラスの土台__としてだけ機能させよう。というのが抽象クラスです。
###実際に書いてみる
// 基底クラス (抽象クラス)
class CRen{
public:
virtual double Position_Sizing() = 0; // 純粋仮想関数
};
// 派生クラス
class CPractice :public CRen{
private:
double lot_size;
public:
void set_lot_size(double Lots) { lot_size = Lots; };
double Position_Sizing(); // 純粋仮想関数をオーバーライド
};
double CPractice::Position_Sizing()
{
//ここにポジションサイジングのロジックを書く
lot_size += 0.1;
return lot_size;
}
int OnInit()
{
double Lots = 0.1;
CPractice practice; // 派生クラスオブジェクト生成
practice.set_lot_size(Lots);
Lots = practice.Position_Sizing();
Print("ロット数は",Lots);
return(INIT_SUCCEEDED);
}
ここでは__Position_Sizing()__という純粋仮想関数を使って抽象クラスを作ってみました。抽象クラス内では__Position_Sizing()__を定義付けしていないですが、その後オーバーライドしているため、コンパイルエラーは発生していません。
- 純粋仮想関数をオーバーライドしていない
- プログラム内で派生クラスではなく抽象クラスを呼び出している
このような時はコンパイルエラーとなります。
- 派生クラスで作るつもりだった関数を書き忘れた
- 呼び出すつもりじゃなかった基底クラスを間違えて呼んだ
こういったヒューマンエラーをコンパイルエラーで気付かせてくれるのも抽象クラスを使うメリットになります。
抽象クラスは派生クラスの数が多くなってくるほどに恩恵を受けると思います。特に複数人で開発するときなどでは非常によく使われる技術なので、ぜひ覚えてみてください。