- PR -

C++ におけるクラスの多重化について

投稿者投稿内容
New LKH
会議室デビュー日: 2007/02/02
投稿数: 13
投稿日時: 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なのかを意識しないで済むような形にしたいのです。
Tdnr_Sym
ぬし
会議室デビュー日: 2005/09/13
投稿数: 464
お住まい・勤務地: 明石・神戸
投稿日時: 2007-08-04 23:17
こんばんは。

引用:

New LKHさんの書き込み (2007-08-04 04:43) より:
B(A) とコンストラクトした際に
(もしくは継承した別のクラスで B_COMMON(A_COMMON) とかでも良い)
A が A1 ならば B1, A2 ならば B2 が生成されるようにするには
どのような方法がありますでしょうか?


どのような方法がありますでしょうか?って仰るということは、
C++でこのような記述ができないのは、分かっておられるんですね?

引用:

呼ぶ側からは、A1かA2なのかB1かB2なのかを意識しないで済むような形にしたいのです。



↓こういうのがGoFのデザインパターンにあるのはご存じですか?

Abstract Factoryパターン
-- お互いに関連したり依存しあうオブジェクト群を、その具象クラスを明確にせずに生成するためのインターフェイスを提供する。
New LKH
会議室デビュー日: 2007/02/02
投稿数: 13
投稿日時: 2007-08-05 00:38
こんばんは

引用:

C++でこのような記述ができないのは、分かっておられるんですね?



スーパークラス(仮想九クラスもしくは共通クラス?)が
サブクラスを意識しないといけないんで逆方向なんですよね。


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 についても参考にして、じっくりと整理したいと思います。
未記入
大ベテラン
会議室デビュー日: 2005/03/12
投稿数: 148
投稿日時: 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 ]
New LKH
会議室デビュー日: 2007/02/02
投稿数: 13
投稿日時: 2007-08-05 14:59
引用:

未記入さんの書き込み (2007-08-05 12:00) より:
これで十分なんじゃないの。
B * b = NULL;
if(aがA1だったら) b = new B1;
else if(aがA2だったら) b = new B2;
else .....



上記を呼ぶ側で行いたくないのです。
何故ならば 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 が親側と言うのはなんか変。

引用:

>(ポインタを private で持ち、public 関数は全て ポインタ->関数 にして定義)
>毎回ラッパークラスのメンバー関数経由で呼ばれるのも馬鹿らしい...

俺には意味がわからなかった。
ラッパークラスいらないんじゃないの?
教えてください。



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 ]
Tdnr_Sym
ぬし
会議室デビュー日: 2005/09/13
投稿数: 464
お住まい・勤務地: 明石・神戸
投稿日時: 2007-08-05 16:38
こんにちは。

引用:

New LKHさんの書き込み (2007-08-05 14:59) より:

コード:
class_b_common
{
	<snip>





場合によっては、アリじゃないですか。
#New LKHさんが一般論で議論しているのか、具体的な目の前の問題解決を図ろうとしているのは知りませんが。

中身を変更したいという意味では、
Strategyパターンに近いのかな!?
よくありそうな「委譲」の使い方だと思います。

それよりも
引用:

New LKHさんの書き込み (2007-08-05 00:38) より:

class_b1,class_b2 とも class_b を継承しているといっても
public な関数は引数含めて全て一緒であって
あくまでも class_a1相手なのか class_a2相手なのかで
実装を変えたいだけなのです。


私ならまず、同じclass_aの具象クラスclass_a1とclass_a2の違いを
外部から意識しなければいけないという点で、見直すことはできないか検討しますが。
chamaro
会議室デビュー日: 2002/07/28
投稿数: 10
投稿日時: 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();

てのはどうでしょうか?
New LKH
会議室デビュー日: 2007/02/02
投稿数: 13
投稿日時: 2007-08-05 22:25
引用:

Tdnr_Symさんの書き込み (2007-08-05 16:38) より:
#New LKHさんが一般論で議論しているのか、具体的な目の前の問題解決を図ろうとしているのは知りませんが。



目前の問題で、とりあえず class_b_common 方式で動かしています。
が、なんらかの追加機能が出来る毎に
class_b, class_b1, class_b2 の変更は(作りが悪いが故に)仕方無いとしても
class_b_common もヘッダーと実装を変更しないといけないんですよね。

中身は class_b->新機能 だけですけどもね。


引用:

Tdnr_Symさんの書き込み (2007-08-05 16:38) より:
中身を変更したいという意味では、
Strategyパターンに近いのかな!?
よくありそうな「委譲」の使い方だと思います。



上記からデザインパターンと言う参考になりそうなURLにたどり着きました。


引用:

Tdnr_Symさんの書き込み (2007-08-05 16:38) より:
私ならまず、同じclass_aの具象クラスclass_a1とclass_a2の違いを
外部から意識しなければいけないという点で、見直すことはできないか検討しますが。



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 のポインタで処理すれば良いだけの話...

修正出来ない部分は修正できないとしても、デザインパターンを研究したいと思います。

スキルアップ/キャリアアップ(JOB@IT)