DATE : 2006/09/30 (Sat)
Java では、クラスの持つ仕様を定義したインタフェースと呼ばれる型があります。
C++ でインタフェースを実現するには、純粋仮想関数を使用します。純粋仮想関数とは、実装を派生クラスに任せるための関数です。
Java で言うとこれは abstract メソッドに相当します。例えば、次のような abstract クラスがあったとします。
public abstract class AbstractCommand { private final String name; public AbstractCommand(String name) { if (name == null) { throw new IllegalArgumentException(); } this.name = name; } public abstract void execute(); public String getName() { return this.name; } }
C++ では次のようになります。(インクルード文などは省略しています)
class AbstractCommand { private: string name; public: AbstractCommand(string& name); virtual ~AbstractCommand() { } virtual void execute() = 0; string getName() const; }; AbstractCommand::AbstractCommand(string& name) : name(name) { } string AbstractCommand::getName() const { return this->name; }
ここで、次の部分に注目してください。
virtual void execute() = 0;
virtual と修飾子を付けたメンバ関数の中身を「= 0」とすると、「純粋仮想関数」になります。ちなみに、「= 0」を付けずに処理を定義すると、「仮想関数」になります。例えば、あらかじめ標準的な処理を定めておきたい場合には、次のように仮想関数にします。
class AbstractCommand { private: string name; public: AbstractCommand(string& name); virtual ~AbstractCommand() { } virtual void execute(); string getName() const; }; AbstractCommand::AbstractCommand(string& name) : name(name) { } void AbstractCommand::execute() { cout << "Command not found." << endl; } string AbstractCommand::getName() const { return this->name; }
「仮想関数」にすると、その関数を派生クラスでオーバーライドすることができます。言い換えると、「仮想関数」でなければオーバーライドできません。
(;^ω^)Java の場合、メソッドに final 修飾子を付けない限りはサブクラスで自由にオーバーライドできますが、C++ はその逆ですね。
ところで、virtual の付いた次のデストラクタは何でしょうか。
virtual ~AbstractCommand() { }
これは「仮想デストラクタ」と呼ばれています。virtual を付けることで、派生クラスにもデストラクタがあることを示しています。仮想デストラクタがない場合は、呼び出したときの型から上位のクラスにあるデストラクタのみが呼び出されます。例えば、上のコードに仮想デストラクタがなかった場合、次のコードでは AbstractCommand のデストラクタしか呼ばれません。
// ListCommand は AbstractCommand を継承 AbstractCommand* command = new ListCommand(); delete command;
そのため、派生クラスがある場合は必ずと言っても良いほど「仮想デストラクタ」を用意しなければなりません。
ちなみに、純粋仮想関数が1つでもクラスにあると、そのクラスのインスタンスは生成できません。
以上から、インタフェースを実現するには、メンバ変数をなくして、メンバ関数を全て純粋仮想関数にすれば良いことが分かります。
例えば、これまでに出てきた Point クラスをインタフェースにすると、次のようになります。(インクルード文などは省略しています)
class Point { public: virtual ~Point() { } virtual Point* clone() const = 0; virtual int getX() const = 0; virtual int getY() const = 0; virtual string toString() const = 0; };
次回は、このインタフェースを利用して装飾可能な点オブジェクトを作ってみたいと思います。例えば、前回までの Point オブジェクトの場合、文字列表現は必ず「(0, 0)」という形になっていました。そこで次回は、「0, 0」や「name(0, 0)」、「(name[0, 0])」と生成する Point オブジェクトを考えてみます。
(;^ω^)実際に点を描く処理であれば点の形を変えたりと非常に華やかなのですが、文字列表現をいじるだけにとどめたいと思います。