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付けたり。あんまり使いたくないなって思った。まあ、めげずに次は抽象クラスについてもうちょっと見てみるつもり。