C++ 動的なオブジェクトの受け渡し

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

C++ 動的なオブジェクトの受け渡し

#1

投稿記事 by paw » 13年前

C++の動的なオブジェクトについて質問です。
動的なオブジェクトの受け渡しというものは通常やらないのでしょうか?
現在、シューティングを作っていまして、
工場クラスで動的に生成した機体オブジェクトが、工場内でしか扱えず、困っていました。
通常の変数のようにゲッターで渡そうとしましたが、どうにも動作がおかしいですし、動的なオブジェクトを渡すという事に違和感があります。

やりたいことをしている、似たような状況を起こすソースを用意しました。
hogeクラスが工場クラスに相当します。
工場クラスでは実際には複数のpiyoオブジェクトを生成します。

コード:

#include <iostream>
using namespace std;

class piyo{
private:
	int data;
public:
	piyo(int a){
		data = a;
	}
	piyo(){}
	void putData(){
		cout << data << endl;
	}
};

class hoge{
private:
	piyo *dou; 
public:
	hoge(int v_a){
		dou = new piyo(v_a);
	}
	~hoge(){
		delete dou;
	}

	piyo *GetPiyo(){
		return dou;
	}
	void delPiyo(){
		delete dou;
	}
};


int main(){
	hoge *ho = new hoge(10);
	piyo *py = new piyo;

	py = ho->GetPiyo();

	py->putData();
	ho->delPiyo();

	delete ho;
	delete py;
return 0;
}
お聞きしたいのは、
・動的なオブジェクトを引き渡すということはやってもいいのか、やるべきなのか。

⇒やってもいいならどうやって実装するのか
工場クラスのprivateに生成するわけですから、それを取得する方法はゲッターを使っていました。
しかしその方法を使ったこのソースでは異常に重くなります

⇒やるべきではないならどのようにしているのか
工場クラスのprivateに生成する以上、工場クラス以外では扱えなくなります。
工場クラスでオブジェクトを管理するのはおかしいですよね

よろしければどなたかご教授ください。

アバター
a5ua
記事: 199
登録日時: 14年前

Re: C++ 動的なオブジェクトの受け渡し

#2

投稿記事 by a5ua » 13年前

まず、提示されたコードにはたくさんの問題点があります。

動的に生成されるオブジェクトに名前をつけてコードを順に追ってみると、

コード:

int main(){
	hoge *ho = new hoge(10);	// H1を生成(内部でP1を生成)
	piyo *py = new piyo;		// P2を生成

	py = ho->GetPiyo();			// P2への参照が失われる。(もはやP2を削除することはできない→メモリリークの原因)

	py->putData();
	ho->delPiyo();				// 内部でP1を削除

	delete ho;					// 内部でP1を削除したのち(→二重delete)、H1を削除
	delete py;					// P1を削除
	return 0;
}
このように、new/deleteの対応が取れていません。P1にいたっては、3回もdeleteされようとしています。

さて、ご質問への回答ですが、
paw さんが書きました: ・動的なオブジェクトを引き渡すということはやってもいいのか、やるべきなのか。
これは、オブジェクト指向プログラミングでは一般的に行われていることだと思いますので、
やるべきではないということはありません。

ただし、動的オブジェクトを扱う際は、オブジェクトの所有権というものを意識しましょう。
特にC++では、newされたオブジェクトを誰がdeleteするのか?ということを考えましょう。

所有権を意識して、hogeクラスにコメントを入れてみると、

コード:

class hoge{
private:
	// douは、hogeのコンストラクタ/デストラクタで、生成/削除する
	piyo *dou;
public:
	hoge(int v_a){
		dou = new piyo(v_a);
	}
	~hoge(){
		delete dou;
	}
	
	// 呼び出し側は、受け取ったオブジェクトをdeleteしてはならない!
	piyo *GetPiyo(){
		return dou;
	}
	
	// douはデストラクタで削除されるので、この関数は決して呼び出してはならない!
	void delPiyo(){
		delete dou;
	}
};
これを元に、main関数を修正します。

コード:

int main(){
	hoge *ho = new hoge(10);	// H1を生成(内部でP1を生成)
	piyo *py = ho->GetPiyo();	// P1への参照を得る(delete py;などとしてはいけない)

	py->putData();

	delete ho;					// 内部でP1を削除したのち、H1を削除
	return 0;
}
newとdeleteの対応が取れているのがわかるでしょうか。


ここからは、工場クラスの設計の一例を示します。
工場は、オブジェクトの生成に専念して、保持はしないのが普通だと思います。
ここでは、以下のように、piyo生成関数create_piyo()でnewしたオブジェクトを、そのままreturnしています。
したがって、生成されたオブジェクトの所有権は、呼び出し元にあります。

コード:

// piyoを生成する工場クラス
class hoge{
private:
	int v_a;	// piyo生成用パラメータ
public:
	hoge(int a)
		: v_a(a)	// 通常、メンバー変数を初期化するときは、このようにメンバーイニシャライザを使う
	{
	}
	
	// 呼び出し元は、受け取ったオブジェクトを責任をもってdeleteすること!
	// (つまり、戻り値のオブジェクトの所有権は、呼び出し元にある)
	piyo *create_piyo()
	{
		// コンストラクタで受け取ったパラメータをもとに、piyoオブジェクトを生成
		return new piyo(v_a);
	}
};
これを利用するmain関数は以下のとおりです。

コード:

int main(){
	hoge *ho = new hoge(10);		// H1を生成
	piyo *py = ho->create_piyo();	// 内部でP1を生成(受け取り側はdeleteする義務がある)

	py->putData();

	delete py;					// P1を削除
	delete ho;					// H1を削除
	return 0;
}
new/deleteの対応がわかりやすくなりましたね。

長くなりましたが、まとめると、

・オブジェクトの所有権を意識する

この一点に尽きます。それでは、頑張ってください。

paw
記事: 15
登録日時: 14年前

Re: C++ 動的なオブジェクトの受け渡し

#3

投稿記事 by paw » 13年前

単純にダミー用意して、渡して、消して、とやっていたつもりがとんでもないことになっていたんですね。
自分の考えの甘さがよく分かりました。
ゲッターで実装するのは無理みたいですね。

そして最後の一例、こんな方法を使うなんて思いつきませんでした。
なるほど、これなら見事に工場外部で管理できますね。

とても分かりやすい回答をありがとうございました。
お陰様で数日間悩んでいた問題が解決しました。

paw
記事: 15
登録日時: 14年前

Re: C++ 動的なオブジェクトの受け渡し

#4

投稿記事 by paw » 13年前

失礼しました。
解決チェックを忘れていましたので。


閉鎖

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