コンポジションについて

フォーラム(掲示板)ルール
フォーラム(掲示板)ルールはこちら  ※コードを貼り付ける場合は [code][/code] で囲って下さい。詳しくはこちら
SG
記事: 2
登録日時: 13年前
住所: 新潟

コンポジションについて

#1

投稿記事 by SG » 12年前

いつも大変お世話になっております。
前回CかC++かという話で、ちょっとずつでもクラスを使っていくと良いというご助言をいただきまして勉強中です。
少し気になる点があるため質問させてください。

ユニットクラスがパラメータクラスを持っているという状況において、
例えばパラメータクラスは下のようなコードとしますが

コード:

class CParam 
{
private:

	int hp;//体力

public:
	
	int GetHp()const
	{
		return hp;
	};
	
	CParam()
	{
		hp=1;
	};

	~CParam(){};
};
これを使う際にユニットはどのような形でパラメータクラスを持てば正解でしょうか?
正解不正解がなければメリット・デメリットをお聞かせください。

①パラメータクラスをそのまま持つか
②またはパラメータクラスのポインタを持って、newするか

コード:

class CUnit 
{
public:
	
	CParam Param;
	
	CUnit(){};
	~CUnit(){};
};
Param.GetHP();//このように使うか

コード:

class CUnit 
{
public:
	
	CParam* Param;
	
	CUnit()
	{
		Param=NULL;

		if(Param==NULL)
		{
			Param=new CParam();
		}
	};

	~CUnit()
	{
		if(Param!=NULL)
		{
			delete Param;
			Param=NULL;
		}
	};
};
Param->GetHp();//またはこのように使うか

Paramのサイズが大きい場合、前者の方法だとスタックオーバーフローする
くらいしか今のところ思いついていません。

------キリトリ-------

また提起のコンポジションの件とは関係有りませんが、後者の場合、
コンストラクタでnewを失敗した場合、どのような処理をするのが適切なのでしょうか?
(プログラムを強制終了するようなコードを書くのか、それとも関数を抜けても機能するようにしたほうが親切なのか)

以上ご回答お待ちしております。

ISLe
記事: 2650
登録日時: 13年前
連絡を取る:

Re: コンポジションについて

#2

投稿記事 by ISLe » 12年前

SG さんが書きました:Paramのサイズが大きい場合、前者の方法だとスタックオーバーフローする
くらいしか今のところ思いついていません。
CUnitの一部としてまとめてメモリ確保されるので、必ずしもスタック上に確保されるとは限らないのでは?

CUnitとParamの寿命が同じなら前者が良いと思います。
SG さんが書きました:コンストラクタでnewを失敗した場合、どのような処理をするのが適切なのでしょうか?
(プログラムを強制終了するようなコードを書くのか、それとも関数を抜けても機能するようにしたほうが親切なのか)
何が適切かは対象ユーザーによるのではないでしょうか。
何が起きても慌てたり文句を言ったりしないユーザーが相手なら強制終了で済ませてしまって良いかもしれませんが、そうでないならお詫びのメッセージを出してきちんと終了するような対策をする必要があると思います。

ゲームソフトだと、ネイティブなリソースにアクセスしていて、強制終了するとOSを再起動せざるを得なくなる場合がよくあります。
どこで中断してもきちんと解放処理を通って終了するように作っておくとデバッグの効率も良いですよ。

アバター
GRAM
記事: 164
登録日時: 13年前
住所: 大阪

Re: コンポジションについて

#3

投稿記事 by GRAM » 12年前

CParamに限らず一般のスタンスでいえば場合によって使い分けたらいいと思います(ほかの人の意見も仰いでください)
コードレベルでいうと後者のほうはもう少し安全なコードに書き換えるべきでしょう

前者のほうはメリットとして
①速い(コンストラクタでの動的確保がないために)
②単純

デメリットとして
①代入時の例外に対する保障が弱くなる
(所有するクラスが代入時に例外を投げないことを保証しない限り代入に失敗した場合失敗前の状態に戻れる保証がない)
②所有するクラスの定義が必要(つまりCParamの内容を書き換えるたびにそれを使う全ファイルの再コンパイルが必要)

後者のほうはメリットとして
①代入時の例外に対する保障を強くできる(代入に失敗した場合失敗前の状態に戻れる)
②所有するクラスの定義が要らず、宣言のみでいい(上でいう再コンパイルの必要がなくなる)

デメリットとして
①動的確保をするため遅い
②暗黙のコピーコンストラクタ、コピー代入演算子が使えなくなる


という感じですかね。どちらのデメリットも工夫次第である程度緩和できるのですが、どっちもどっちになると思います
ちなみに後者の場合ならばポインタはスマートポインタ(auto_ptrやshared_ptr)の形で保持して、メモリ管理は彼らに任せたほうがよいかと。

この場合なら前者のデメリットはあまりなさそうですけどね・・・(代入には失敗しないし、書き換えもそう起こると思えない・・・)

SG
記事: 2
登録日時: 13年前
住所: 新潟

Re: コンポジションについて

#4

投稿記事 by SG » 12年前

おはようございます。
お二人ともお返事ありがとうございます。
良い連休をおすごしください。

ISLeさん

<CUnitの一部としてまとめてメモリ確保される
なるほど、そうなのですか。この辺がどうもとっつきにくいです。メモリの勉強してくるか・・・
寿命を意識する旨も了解しました。

<どこで中断してもきちんと解放処理を通って・・・
この発想はありませんでした。しかし無いと駄目ですねww
そういう目で少し見なおしてみます。きちんと動けば解放するようにはなっていると思うんですが、
それだけでは駄目ということですね。

GRAMさん

お二人の意見を拝見していると、今回の件は前者の方がよさそうですね。

とはいえ例外処理・・・これも全然意識していませんでした。勉強になります。
コピーコンストラクタや代入演算子は知らないといつの間にかつかっていそうで怖いですね。
(いっそコピー禁止しちゃうか・・・と思ってしまいます)

スマートポインタは最近になって色々な所で見かけます。
メモリリークを防いでくれるという謳い文句は非常に魅力的なのですが、
実際に見に行ってみると説明ページが難解で尻込みしております。
とはいえオススメされるということは(というかそもそも存在するということは)やっぱり良いものなのですね。
今後の目標の一つにしたいと思います。

閉鎖

“C言語何でも質問掲示板” へ戻る