C++

抽象クラスと仮想関数

更新日:

クラスのポインタ

この記事では仮想関数と抽象クラスと呼ばれる機能について解説します。

それに際し、まずはC++におけるポインタの仕様について触れます。

今まで何度か出てきていますが、ポインタはアドレスを格納する仕組みなので、勿論インスタンスにも適用することが出来ます。

詳しくはポインタクラスの利用クラスの継承を参照。

ポインタ

クラスの使い方

クラスの継承

ここで基本クラスBasisとその派生クラスDerivedを考えましょう。

Basis *pb;
Derived d;


BasisのポインタとDerivedのインスタンスを宣言しました。

pbにはBasisのインスタンスのアドレスを格納するのが普通ですが、その派生クラスであるDerivedのインスタンスを格納することが出来ます。

pb = &d;

ここまでは直感的に理解できるでしょう。

ですがここからが問題です。
BasisとDerivedはget()というメンバ関数を持っているとしましょう。
Derivedの方はBasisの関数をオーバーライドしたものになります。

pb->get();


ではこのように記述した場合呼び出されるのはBasisとDerivedのどちらでしょうか。

実はこれはBasisつまり基本クラスのメンバ関数が呼び出されます。
これはポインタの型がBasisである為です。

無理やりキャストすればDerivedの方を呼び出すことも可能です。

仮想関数

この場合、直感的にはDerivedつまり派生クラスの関数が呼び出されると考えるのが一般的でしょう。
何故なら、ポインタの指している値がDerivedクラスのものだからです。

このほうがわかり易いので、Derivedのメンバを参照するように設定することが出来ます。
virtual修飾子というものを使います。

基本クラスを定義する際に、ポインタで呼び出すと派生クラスを呼び出したい関数を以下のように記述します。

virtual 【戻り値の型】 【メンバ関数名】(【引数】){ 【処理】 }


戻り値の前にvirtualという予約語を付けます。staticなどと同様です。

このようなオーバーライドする前提の関数を仮想関数と呼びます。

これを使うと先ほどのget()の呼び出しの際にDerivedクラスのメンバを呼び出します。

抽象クラス

仮想関数はオーバーライドされる前提の関数であることは理解できましたね。

それでは、オーバーライドされる関数の中身を書く必要があるでしょうか。
ありませんね。

なので、virtual修飾子を付けた関数は処理を記述しないことが可能です。

virtual 【戻り値の型】 【メンバ関数名】(【引数】) = 0;


と書くことで処理を省略します。

このような、処理のない仮想関数を純粋仮想関数と呼びます。

さらに、純粋仮想関数を一つでも持つクラスを抽象クラスと呼びます。
抽象クラスはインスタンスを生成することが出来ません。
メンバ関数の中身がないので当たり前ですね。

オーバーライドされる前提の関数を持つクラスは、継承される前提のクラスであると言えます。

では最後に、何故継承される前提の抽象クラスと言うものが必要なのか考えましょう。

これは、クラスを設計する為の雛形として使われます。

オブジェクト指向とはで「走る」という動作を例に出しましたね。
ここでいう抽象クラスの仮想関数が「走る」という言葉であり、実際の動作については各派生クラスでオーバーライドした関数内で処理されます。

オブジェクト指向とは

すなわち、異なる動作をするクラス、メンバ関数であっても、同一の抽象クラスから派生させることで同等のものとして処理することが出来ます。


-C++

Copyright© ツナのエンジニアブログ , 2025 All Rights Reserved Powered by AFFINGER5.