- - PR -
C++ におけるクラスの多重化について
投稿者 | 投稿内容 | ||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
投稿日時: 2007-08-04 04:43
A と言うスーパークラス(純粋仮想クラス)のサブクラスに A1,A2 があるとします。
B と言うスーパークラス(純粋仮想クラス)のサブクラスに B1,B2 があるとします。 B のコンストラクタの引数は A だとします。 B(A) とコンストラクトした際に (もしくは継承した別のクラスで B_COMMON(A_COMMON) とかでも良い) A が A1 ならば B1, A2 ならば B2 が生成されるようにするには どのような方法がありますでしょうか? 呼ぶ側からは、A1かA2なのかB1かB2なのかを意識しないで済むような形にしたいのです。 | ||||||||||||
|
投稿日時: 2007-08-04 23:17
こんばんは。
どのような方法がありますでしょうか?って仰るということは、 C++でこのような記述ができないのは、分かっておられるんですね?
↓こういうのがGoFのデザインパターンにあるのはご存じですか? Abstract Factoryパターン -- お互いに関連したり依存しあうオブジェクト群を、その具象クラスを明確にせずに生成するためのインターフェイスを提供する。 | ||||||||||||
|
投稿日時: 2007-08-05 00:38
こんばんは
スーパークラス(仮想九クラスもしくは共通クラス?)が サブクラスを意識しないといけないんで逆方向なんですよね。 class_a の方にどちらなのかを返す public メンバー関数を用意しました。 class_b に Base とは全く関係の無い似た名前のラッパークラスを作成して 利用する方法を考えたのですが (ポインタを private で持ち、public 関数は全て ポインタ->関数 にして定義) 毎回ラッパークラスのメンバー関数経由で呼ばれるのも馬鹿らしい... で、次に考えたのは class_b (Base)のポインタを返す new_class_b なる関数を作る方法です。 こちらは new_class_b 関数と delete が違うレベルになるのが嫌らしい... (delete_class_b も作っても良いんですけど) class_b1 と class_b2 をメンバーに持つ class_b_common 作るのも変だし class_a1,class_a2 は外部提供のライブラリで それを class_b を利用するレベルでは共通的に扱いたいと言うのが要件なのですが なんかうまく整理できないんです。 どんな方法が美しいと言うか設計方針とかで良いのか メンテナンス性が良いのかを思索中なのです。 class_b1,class_b2 とも class_b を継承しているといっても public な関数は引数含めて全て一緒であって あくまでも class_a1相手なのか class_a2相手なのかで 実装を変えたいだけなのです。 #従って、class_b1,class_b2 で拡張された public な関数は存在しない。 #しかし... 結局 private では違う部分がどうしても発生する。 紹介いただいた URL についても参考にして、じっくりと整理したいと思います。 | ||||||||||||
|
投稿日時: 2007-08-05 12:00
>こちらは new_class_b 関数と delete が違うレベルになるのが嫌らしい...
俺には意味がわからなかった。 教えてください。 気分的なことで却下? そんなこといったら大変ですな RTTIとかdynamic_castでAがA1かA2か区別はつくけど。 >B(A) とコンストラクトした際に このコンストラクタの引数が値型だとだめだけどね。 そもそも >class_a の方にどちらなのかを返す public メンバー関数を用意しました。 これで十分なんじゃないの。 B * b = NULL; if(aがA1だったら) b = new B1; else if(aがA2だったら) b = new B2; else ..... >(ポインタを private で持ち、public 関数は全て ポインタ->関数 にして定義) >毎回ラッパークラスのメンバー関数経由で呼ばれるのも馬鹿らしい... 俺には意味がわからなかった。 ラッパークラスいらないんじゃないの? 教えてください。 話は変わりますが 外部に一部分だけ作らせるって面倒ですよね。 [ メッセージ編集済み 編集者: 未記入 編集日時 2007-08-05 12:08 ] | ||||||||||||
|
投稿日時: 2007-08-05 14:59
上記を呼ぶ側で行いたくないのです。 何故ならば B1 と B2 を意識しないといけないから。 で、B のポインタを返すだけの関数(クラスに属さない)。 #include "class_b" #icnlude "class_b1" #icnlude "class_b2" class_b *new_class_b(class_a *a) { class_b * b = NULL; if(aがclass_a1だったら) b = new class_b1((class_a1 *)a); else if(aがclass_a2だったら) b = new class_b2((class_a2 *)a); else ..... return b; } こうしていて #include "class_b" 利用関数(class_a *a) { class_b * b = NULL; b = new_class_b(a); b->適当な関数(); delete b; // どこかでエラーがあったら行う必要あり } てな、ことにすれば、利用関数自体は class_b1 とか class_b2 を 意識しないで済みます。 new_class_b と言う関数で new しているのに delete が親側と言うのはなんか変。
class_b_common { private: class_b * p_b; public: class_b_common(class_a *a); ~class_b_common(); 適当な関数(); } class_b_common::class_b_common(class_a *a) { p_b = NULL; if(aがclass_a1だったら) b = new class_b1((class_a1 *)a); else if(aがA2だったら) b = new class_b2((class_a2 *)a); else ..... p_B = b; } class_b_common::~class_b_common() { delete p_b; } class_b_common::適当な関数() { return p_b->適当な関数(); } #include "class_b" #include "class_b_common" 利用関数(class_a *a) { class_b_common b(a); b.適当な関数(); // デストラクト時に自動で delete される } [ メッセージ編集済み 編集者: New LKH 編集日時 2007-08-05 15:13 ] | ||||||||||||
|
投稿日時: 2007-08-05 16:38
こんにちは。
場合によっては、アリじゃないですか。 #New LKHさんが一般論で議論しているのか、具体的な目の前の問題解決を図ろうとしているのは知りませんが。 中身を変更したいという意味では、 Strategyパターンに近いのかな!? よくありそうな「委譲」の使い方だと思います。 それよりも
私ならまず、同じclass_aの具象クラスclass_a1とclass_a2の違いを 外部から意識しなければいけないという点で、見直すことはできないか検討しますが。 | ||||||||||||
|
投稿日時: 2007-08-05 18:22
Bのコンストラクタを隠蔽してしまい。
かならずCreateで作成最後にDeleteを呼ぶようにするってのはどうですか? (C++久しぶりなので外していたらすみません) static B* B::Create(const A& val) { if(valがa1) return new B1(val); else return new B2(val); } void B::Delete() { detete this; } 使う側 B* p = B::Create(a); : p->Delete(); てのはどうでしょうか? | ||||||||||||
|
投稿日時: 2007-08-05 22:25
目前の問題で、とりあえず class_b_common 方式で動かしています。 が、なんらかの追加機能が出来る毎に class_b, class_b1, class_b2 の変更は(作りが悪いが故に)仕方無いとしても class_b_common もヘッダーと実装を変更しないといけないんですよね。 中身は class_b->新機能 だけですけどもね。
上記からデザインパターンと言う参考になりそうなURLにたどり着きました。
class_a が、きちんと欲しい関数を全部実装してくれれていれば... そもそも class_b も class_b1,class_b2 も要らないかも知れないと思っています。 (しかし class_a は変更不可) 従って class_a を継承した class_a_supper とかを作って (環境依存の) class_a1,class_a2 も拡張すれば良いだけはずと思っているのですが 継承関係が複雑になるとか言う理由で許されていないのです。 class_a1,class_a2 は class_a じゃなく (うちの環境だけで使う class_b 相当までサポートする) class_a_supper から 継承すれば良いだけのはずだし。 (今更、変更出来ないという話もあるかも知れない) class_a のポインタで参照される限りはメンバー関数じゃないので class_a_supper 相当部分は呼べない(なので class_a 仕様は変更なし)。 class_a_supper を知っている(新規に追加する class_b を呼びたかった)関数は class_a_supper のポインタで処理すれば良いだけの話... 修正出来ない部分は修正できないとしても、デザインパターンを研究したいと思います。 |