ページ 11

スマートポインタでのポリモーフィズムについて

Posted: 2012年10月23日(火) 10:08
by 赤鬼
スマートポインタでポリモーフィズムを実現する際にふと壁に当たりました。

コード:

///インターフェイスクラス
class ICharaBase{
public:
	virtual ~ICharaBase(){}
public:
	virtual void Load()=0;
	virtual void Ini()=0;
	virtual void Calc()=0;
	virtual void Draw()=0;
};
//自キャラ
class MChara:public ICharaBase{
//インターフェイスのオーバーライド
	void Load();
	void Ini();
	void Calc();
	void Draw();
//ほか項目は関係無いのでパス
};
//敵キャラ
class CEnemy:public ICharaBase{
//インターフェイスのオーバーライド
	void Load();
	void Ini();
	void Calc();
	void Draw();
//ほか項目は関係無いのでパス
};

クラス構造が大まかにこうなっている時(インターフェイスはリストにポインタを持たせて使用します)。
別のクラスから内部にアクセスするため、またMChara特有の機能を使う場合(MCharaのみへの影響がある処理等、CEnemyと少し違った処理をしたい場合。)、自分はMCharaに実態を持たせアクセスしようと思ったのですが。
普通のポインタなら

コード:

std::list<ICharaBase> ICB;
MChara* mchar=new MChara();
//UPキャストだから、気にしないでOK
ICB.push_back(reintepret_cast<ICharaBase>(mchar));
とできますが。
スマートポインタの場合はこれができないと思います。
代わりに、インターフェイスを利用する方法として下記がありますが、これでは、下位クラスにアクセスすることはできないと思います。

コード:

std::list<std::shared_ptr<ICharaBase>> ICB;
ICB.push_back(std::shared_ptr<ICharaBase>(new MChara()));
この状況でスマートポインタを使う場合、関数をインターフェイスに無駄に用意してやるか(コードの保守性の低下)、
ダウンキャストをする(煩雑になり、RTTIを使用するため実行速度の低下の可能性)以外考えつかなかったのですが、他に何か良い方法はないでしょうか。
根本的にスマートポインタを使わずに済ますか、それとも、クラスの構成そのものを変えたほうがいいでしょうか?
皆さんの意見が聞きたいです。

Re: スマートポインタでのポリモーフィズムについて

Posted: 2012年10月23日(火) 14:17
by h2so5
そもそもこのコードではコンパイルが通りません。
インターフェースクラスが実体化されている時点でおかしなコードです。
赤鬼 さんが書きました:

コード:

std::list<ICharaBase> ICB;
MChara* mchar=new MChara();
//UPキャストだから、気にしないでOK
ICB.push_back(reintepret_cast<ICharaBase>(mchar));
そして、クラスポインタのアップキャストにreinterpret_castを使うべきではありません。
reinterpret_castは関係の無い型のポインタ同士を無理やりキャストするためのもので、
ダウンキャストや継承関係にない型同士のキャストをしてもエラーにならないため危険です。


このコードならコンパイルが通ります。
MCharaのメンバ関数の実体が定義されていないのでリンカエラーになりますが。

コード:

std::list<ICharaBase*> ICB;
MChara* mchar = new MChara();
ICB.push_back(mchar);
スマートポインタを使用すればこうなるでしょう

コード:

typedef std::shared_ptr<ICharaBase> ICharaBasePtr;
typedef std::shared_ptr<MChara> MCharaPtr;

std::list<ICharaBasePtr> ICB;
MCharaPtr mchar = std::make_shared<MChara>();
ICB.push_back(mchar);

Re: スマートポインタでのポリモーフィズムについて

Posted: 2012年10月23日(火) 15:27
by 赤鬼
h2so5 さんが書きました:そもそもこのコードではコンパイルが通りません。
インターフェースクラスが実体化されている時点でおかしなコードです。
赤鬼 さんが書きました:

コード:

std::list<ICharaBase> ICB;
MChara* mchar=new MChara();
//UPキャストだから、気にしないでOK
ICB.push_back(reintepret_cast<ICharaBase>(mchar));
そして、クラスポインタのアップキャストにreinterpret_castを使うべきではありません。
reinterpret_castは関係の無い型のポインタ同士を無理やりキャストするためのもので、
ダウンキャストや継承関係にない型同士のキャストをしてもエラーにならないため危険です。


このコードならコンパイルが通ります。
MCharaのメンバ関数の実体が定義されていないのでリンカエラーになりますが。

コード:

std::list<ICharaBase*> ICB;
MChara* mchar = new MChara();
ICB.push_back(mchar);
あ、listの所*ぬけてました。
すみません。
この場合reinterpret_castは使うべきではないのですね(つまり、ポインタ型→int型→ポインタ型の場合とかですか(この場合voidでしょうが))。
そういえば、UPキャストは暗黙的に変換されるんでしたっけ。
h2so5 さんが書きました: スマートポインタを使用すればこうなるでしょう

コード:

typedef std::shared_ptr<ICharaBase> ICharaBasePtr;
typedef std::shared_ptr<MChara> MCharaPtr;

std::list<ICharaBasePtr> ICB;
MCharaPtr mchar = std::make_shared<MChara>();
ICB.push_back(mchar);
インターフェイスクラスをスマートポインタで確保してUPキャストさせるんですね。
お早い回答どうもありがとうございました。