GoF本のためのC++入門 (その3)

前回に続いて、GoF本に載っているサンプルコードを読むためにC++を勉強してみる。GoF本ってのは「オブジェクト指向における再利用のためのデザインパターン」のことね。

今日はクラスの継承をやってみる。GoF本でもばんばん使われているので大事だ。参考にしたページは以下。
C++編(言語解説) 第8章 継承
C++編(言語解説) 第9章 オーバーライド
C++の基礎 : 仮想関数


今回はR2クラス(2次元の座標平面)を親クラスとして作って、そいつを継承してComplex(複素数)クラスを作ってみることにする。

まずはR2クラスから。

#include <iostream>
#include <sstream>
#include <string>

using namespace std;

// 2次元の座標平面
class R2 {
public:
    R2(double x, double y);

    // こいつを子クラスでオーバーライドする
    virtual string toString();

protected:
    double v[2];
};

// メソッドの実装
R2::R2(double x, double y)
{
    v[0] = x;
    v[1] = y;
}

string R2::toString()
{
    ostringstream os;
    os << "(" << v[0] << ", " << v[1] << ")";
    return os.str();
}

toString関数で (5, 3) みたいな座標っぽい文字列が取ってこれるよ。

次にComplexクラス。

// R2クラスを継承する
class Complex : public R2 {
public:
    // このコンストラクタの書き方はよく分かってない
    Complex(double x, double y) : R2(x, y){};
    // オーバーライドするメンバ関数の宣言が必要
    string toString();
};

// 関数オーバーライド
string Complex::toString()
{
    ostringstream os;
    os << v[0] << " + " << v[1] << "i";
    return os.str();
}

数値を2個持ってるっていうのは一緒だけど、toString関数をオーバライドして 5 + 3i みたいな複素数っぽい文字列に変更したよ。

実際にこのクラスを使ってみる。

int main()
{
    // R2(親クラス)をインスタンス化
    R2* v = new R2(3.0, 4.0);
    cout << "v = " << v->toString() << endl; //=> v = (3, 4)
    delete v;

    // Complex(子クラス)をインスタンス化
    Complex* z1 = new Complex(3.0, 4.0);
    cout << "z1 = " << z1->toString() << endl; //=> z1 = 3 + 4i
    delete z1;

    // R2* 型と宣言してもOK
    R2* z2 = new Complex(5.0, 12.0);
    cout << "z2 = " << z2->toString() << endl; //=> z2 = 5 + 12i
    delete z2;

    return 0;
}

最後の例は大事だ。変数の型としてはR2*(親クラス)として宣言してるけど、実際にtoString()を呼び出すとComplex(子クラス)の方が呼び出されている。これがR2のtoStringの宣言でvirtualを付けた理由だ。もしvirtualが無かったらR2の方のtoStringが呼び出されて、z2 = (5, 12)と出力されてしまう。それじゃあ全然継承できたことになってないね。一見R2のようだけど実はComplexのオブジェクト、というふうに使いたいんだよ。

というわけで、継承するつもりのメンバ関数にはvirtualを付ける。実際、GoF本でも付けまくってた。

で、ちょっとやってみて思ったけどC++めんどくさいね。型があったり、virtual付けたり。あんまり使いたくないなって思った。まあ、めげずに次は抽象クラスについてもうちょっと見てみるつもり。

< 前へ | 次へ >
この連載の一覧