階層構造の継承(昨日の続き)
#includeusing namespace std; template class M{ public: T* Alloc(void){return new T;} }; class B1{ public: int x; virtual ~B1(){} }; class A1{ public: B1 *b1; M b1alc; A1(){b1 = b1alc.Alloc();} virtual void f(int i){b1->x = i;} }; class B2 : public B1{ public: int y; }; class A2 : public A1{ public: M b2alc; A2(){b1 = b2alc.Alloc();} virtual void f(int i, int j){ A1::f(i); B2 *b2 = dynamic_cast (b1); b2->y = j; } }; int main(void){ A2 a2; a2.f(10, 20); B2 *b2 = dynamic_cast (a2.b1); cout << b2->x << " " << b2->y << endl; }
えと、
A1 -------- B1
← 継承 | ← 継承 |
A1がメンバ変数にB1を持っている、という構造でA1とB1をそれぞれ拡張したクラスを作りたいとする。こういうときはやっぱりテンプレート使った方が効率いいんだろうか・・・。しかしそれだとクラス全体をテンプレートで記述しないといけなくなるし。ということで苦肉の策としてアロケータを作ることに。しかしこれでも問題があって
- ダウンキャストが避けられない
- あらかじめこういう使われ方をされることを予測してスーパークラスを設計しなければいけない
と思う。特に2番目はオブジェクト指向としてどうなのか。サブクラスの仕様にスーパークラスの仕様が依存するというのもおかしな話だ。
テンプレートを使っても2番目の問題は変わらないわけで。
ということで、強引な解決法としては、A1にB1へのポインタ、A2にはB2のポインタを持たせておいて、A2ではB1とB2のポインタが同じものを指すようにする。こうすればとりあえずA1側で仕様を変える必要はないし、ダウンキャストもいらない。あとB1とかを無駄にpolymorphicにする必要もない。ただ同じものを指すポインタを何個も持つってのは微妙だし、新しいオブジェクトをnew()して古いのをdelete()するってのも無駄なオーバーヘッドではある。結局テンプレートが一番無駄がないってことでしょうかね。
#includeusing namespace std; class B1{ public: int x; }; class A1{ public: B1 *b1; A1(){ b1 = new B1; } virtual void f(int i){b1->x = i;} }; class B2 : public B1{ public: int y; B2(const B1 * const b1){ x = b1->x; } }; class A2 : public A1{ public: B2 *b2; A2(){ A1::A1(); b2 = new B2(b1); delete b1; b1 = b2; } virtual void f(int i, int j){ A1::f(i); b2->y = j; } }; int main(void){ A2 a2; a2.f(10, 20); cout << a2.b2->x << " " << a2.b2->y << endl; }
まあ、泥臭いけどテンプレートを使いたくないなら一番いい解決法なのかも????難しいです・・・。サブクラス側で対応っていうのが個人的にはいいと思うので、これで行ってみますか。