多数のクラスの中から必要なクラスだけインスタンス化し管理する方法 – プログラミング – Home

多数のクラスの中から必要なクラスだけイ...
 
通知
すべてクリア

[解決済] 多数のクラスの中から必要なクラスだけインスタンス化し管理する方法


憂
 憂
(@憂)
ゲスト
結合: 20年前
投稿: 72
Topic starter  

こんにちは
いつもお世話になっております

環境
WinXP、VC++6.0、MFC、SDI

質問しておきながら、かなり無理っぽい予感がするのですが・・・
無理としても、代わりになる案か何かがあれば教えていただきたく思い投稿しました

まずこの例を見てください
あくまでもこのようなことをしたい、という例なのでおかしな部分があると思いますが
気にしないで見てください

//全てのクラスの基本となるクラス
class CBase{...};

//CBaseを派生元とするクラス群
class ClassA : public CBase{...};
class ClassB : public CBase{...};
・・・(略)・・・
class ClassY : public CBase{...};
class ClassZ : public CBase{...};

というように同じ基本クラスから派生して作った多数のクラスがあるとします
これを・・・

typedef CTypedPtrArray<CPtrArray, CBase*> ClassPtrArray;

void CreateClassArray(int nFlag, ClassPtrArray& rArray)
{
[なんらかの型] ClassName[3][5] = {
{ClassA, ClassC, ClassH, ClassU, ClassW},
{ClassB, ClassI, ClassJ, ClassM, ClassQ},
{ClassE, ClassF, ClassK, ClassO, ClassZ},
};

int nCnt;
CBase* pBase;

for(nCnt = 0; nCnt < 5; nCnt++)
{
pBase = (CBase*)new ClassName[nFlag][nCnt];
rArray.Add(pBase);
}
}

のように状況に応じて領域を確保し、ポインタを配列に保存しておきたいのです
上の例では一度にインスタンスを作成するクラスの種類は5つに固定されていますが、
実際は可変になります(nFlagが0のときは5種類、1のときは8種類などがあり得る)

ちなみに今使っている方法は、
全てのクラスのインスタンスをあらかじめ作成しておき、
それらの全てのポインタを配列に入れて保存
そこからインデックスを使って必要なポインタを取り出すというものです

ClassA classA;
ClassB classB;
・・・(略)・・・
ClassY classY;
ClassZ classZ;

CBase* ClassPtrTable[] = {
&classA,
&classB,
(略)
&classY,
&classZ,
}

しかしながら、クラスの数だけでも数百に登り、かなりメモリに無駄が生じます

そこで上に書いたようなことができないものかと思って質問したのですが、
実現する方法はないでしょうか?

必ずしも例のような形でなければならないということはありません
最終的な目標としては

「状況に応じてそのとき使うクラスのインスタンスだけ作成し、
 そのポインタを配列で管理する」

ということが達成できればいいのです

何かよい方法を知っている方がいましたら教えていただけるとありがたいです
よろしくお願いします


引用未解決
トピックタグ
しっぽ
 しっぽ
(@しっぽ)
ゲスト
結合: 22年前
投稿: 10
 

「CBaseを派生元とするクラス群」を
class ClassA : public CBase
{
public:
static CBase* Create(){return new ClassA;};
};
とし、[なんらかの型]の部分を関数ポインタの配列にすればできると思います。


返信引用
へも
 へも
(@へも)
ゲスト
結合: 20年前
投稿: 3
 

インスタンスを作る関数をクラス分用意して
その関数のテーブルを作るとか。

CBase* createA() { return new ClassA; }
CBase* createB() { return new ClassB; }
  :
  :

typedef CBase* (*CreateFunc)();
CreateFunc table[3][5] = {
{createA, createB, createC, createD, createE},
{createF, createG, NULL, NULL, NULL},
  :
};

for(nCnt = 0; nCnt < 5 && table[nFlag][nCnt] != NULL; nCnt++)
{
rArray.Add(table[nFlag][nCnt]());
}


返信引用
REE
 REE
(@REE)
ゲスト
結合: 23年前
投稿: 240
 

関数をあまり増やしたくなければ・・・

enum class_ID { ClassA_ID, ClassB_ID, ClassC_ID, ... };

CBase* CreateClass(class_ID id) // CBaseのstaticメンバでもよい
{
switch(id)
{
case ClassA_ID: return (CBase*)new ClassA;
case ClassB_ID: return (CBase*)new ClassB;
...
}
return NULL; // 又は例外など
}

void CreateClassArray(int nFlag, ClassPtrArray& rArray)
{
class_ID ClassName[3][5] = {
{ClassA_ID, ClassC_ID, ClassH_ID, ClassU_ID, ClassW_ID},
{ClassB_ID, ClassI_ID, ClassJ_ID, ClassM_ID, ClassQ_ID},
{ClassE_ID, ClassF_ID, ClassK_ID, ClassO_ID, ClassZ_ID},
};

int nCnt;
CBase* pBase;

for(nCnt = 0; nCnt < 5; nCnt++)
{
pBase = CreateClass(ClassName[nFlag][nCnt]);
rArray.Add(pBase);
}
}


返信引用
憂
 憂
(@憂)
ゲスト
結合: 20年前
投稿: 72
Topic starter  

しっぽさん、へもさん、REEさん、レスありがとうございます

なるほど、このような方法があるのですね・・・
確かにこれら方法なら今考えている理想的な処理を実現できます
これからまたこれらの手法を取り込んでクラス構造を練り直します

そしてあとはその数百あるクラスを作成するだけ・・・げんなりですね

しかし、これで行き詰っていたところから先に進めます
どうもありがとうございました


返信引用
あた
 あた
(@あた)
ゲスト
結合: 20年前
投稿: 2
 

解決になってますが
MSDN で 動的生成 を調べてみてはどうですか。

CObject からの派生クラスにできるなら
DECLARE_DYNCREATE マクロを埋め込んでおけば
ランタイム クラス情報から動的にオブジェクトを作成できると思います。

MFC の DocTemprate のような管理ができるんじゃないでしょうか。
クラスの型情報も取得できるのでいいと思います


返信引用
憂
 憂
(@憂)
ゲスト
結合: 20年前
投稿: 72
Topic starter  

あたさん、解決チェック済みにも関わらずレスありがとうございます

今までAppWizardが作成するままに使っていたDECLARE_DYNCREATEなどのマクロですが
実際の中身を調べてみてなるほど・・・と、それなりに理解が深まった気がします

確かに今回の目的を達成するためにこの機能を使うのは正解かもしれません
ただMFCでしか使えなくなるという欠点はありますが・・・まぁ他に流用する予定も
特にありませんし

そこでCObjectの動的生成に関して調べ、簡単なテストプログラムを作ってみました
しかし、なにぶんMFCは経験が浅く、
このような書き方で正しいのかいまいち自分で判断できません

下にテストプログラムを載せますので、お手数ですがどなたか
おかしいところがないかどうか簡単でいいのでチェックしていただけないでしょうか?

//■全ての基礎となるクラス
class CBase : public CObject
{
DECLARE_DYNAMIC(CBase);
public:
CBase(){}
virtual ~CBase(){}
virtual void Test(CString& str) = 0;
};

//■ClassA
class ClassA : public CBase
{
DECLARE_DYNCREATE(ClassA);
public:
virtual void Test(CString& str){str += [A];}
};

//■ClassB
class ClassB : public CBase
{
DECLARE_DYNCREATE(ClassB);
public:
virtual void Test(CString& str){str += [B];}
};

//■ClassC
class ClassC : public CBase
{
DECLARE_DYNCREATE(ClassC);
public:
virtual void Test(CString& str){str += [C];}
};

//■使用するクラスを動的に作成する関数
void CreateClassArray(int nType)
{
CRuntimeClass* ClassTable[3][3] = {
{RUNTIME_CLASS(ClassA), RUNTIME_CLASS(ClassB), RUNTIME_CLASS(ClassC)},
{RUNTIME_CLASS(ClassC), RUNTIME_CLASS(ClassB), RUNTIME_CLASS(ClassA)},
{RUNTIME_CLASS(ClassA), RUNTIME_CLASS(ClassC), RUNTIME_CLASS(ClassB)},
};

CTypedPtrArray<CPtrArray, CBase*> PtrArray;
int nCnt;
CBase* pBase;
CString str;

//テーブルからクラスのインスタンスを作成
for(nCnt = 0; nCnt < 3; nCnt++)
{
pBase = (CBase*)ClassTable[nType][nCnt]->CreateObject();
PtrArray.Add(pBase);
}

//正しく作成されているかテスト
for(nCnt = 0; nCnt < 3; nCnt++)
{
PtrArray[nCnt]->Test(str);
}
AfxMessageBox(str);

//開放
for(nCnt = 0; nCnt < PtrArray.GetSize(); nCnt++)
{
delete PtrArray[nCnt];
}
}

これを

CreateClassArray(0);
CreateClassArray(1);
CreateClassArray(2);

と呼び出すと、メッセージボックスがそれぞれ1回ずつ、計3回出て、

1回目:[A][B][C]
2回目:[C][B][A]
3回目:[A][C][B]

と予想通りの結果が出ました

見た目上は期待通りの動作をしています
この書き方で問題がなければいいのですが・・・どうでしょうか?

何かおかしい点等ありましたら指摘していただけるとありがたいです
よろしくお願いします


返信引用
憂
 憂
(@憂)
ゲスト
結合: 20年前
投稿: 72
Topic starter  

すみません、訂正です

//■使用するクラスを動的に作成する関数
void CreateClassArray(int nType)
{
・・・略・・・
}

の上あたりに

IMPLEMENT_DYNAMIC(CBase, CObject)

IMPLEMENT_DYNCREATE(ClassA, CBase)
IMPLEMENT_DYNCREATE(ClassB, CBase)
IMPLEMENT_DYNCREATE(ClassC, CBase)

が入っているものと思ってください
書き忘れすみませんでした


返信引用
あた
 あた
(@あた)
ゲスト
結合: 20年前
投稿: 2
 

オブジェクトの作成方法は問題ないと思います。

DECLARE_DYNCREATE() マクロは
クラスに下記を追加します

static const CRuntimeClass classClassName;
static CObject* __stdcall CreateObject()
{
return new ClassName;
}

このとき CRuntimeClass のメンバー
m_pfnCreateObject に CreateObject() 関数ポインターを保存します。

RUNTIME_CLASS() マクロは、
クラスの ClassName::classClassName staticメンバーのポインターを取り出します。

(&ClassName::classClassName)->CreateObject() この関数の呼び出しで
m_pfnCreateObject の関数を実行します。

動作の仕組みは、しっぽさんの方法です。


返信引用
憂
 憂
(@憂)
ゲスト
結合: 20年前
投稿: 72
Topic starter  

あたさん、お返事と、さらに細かい解説までありがとうございます

解説していただいた内容とMFCのソースを見比べて、
CRuntimeClassの動きをおおよそ理解することができました

おかげさまで作業が進みそうです

最後までお付き合いいただきありがとうございました


返信引用

返信する

投稿者名

投稿者メールアドレス

タイトル *

プレビュー 0リビジョン 保存しました
共有:
タイトルとURLをコピーしました