メンバ変数をキーに自分自身をdeleteしたい

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

メンバ変数をキーに自分自身をdeleteしたい

#1

投稿記事 by taketoshi » 7年前

あるクラスのメンバ変数(たとえばヒットポイントが0になったら)を監視し
条件を満たしたらインスタンスを自分自身でdeleteしたいです。

試しに、以下のようなコードを書いてみました。
xをキーにして、thisポインタを渡しdeleteしています。
そしてそのポインタは後で再利用したいので、NULLを代入しておきます。

ステップ実行で追っていくと、インスタンスがdeleteされてNULLで初期化されているのですが
再利用しようとするとif文が実行されずインスタンスが生成できません。
ステップ実行で追っていくときちんとNULLで初期化はされているようなのですが・・・。
thisポインタがconstで渡されているのが非常に気になります。

メンバ変数を監視し、ある特定条件を満たしたら自分自身をdeleteし
また後でそのポインタを使いまわすようにするにはどのような処理にすればよいでしょうか。

ご教示お願いいたします。

コード:

#include<windows.h>
#include<stdio.h>

class hoge{
private:
	int x;
public:
	int add();
	static int deletehoge(hoge *);//インスタンス廃棄用
	static int newhoge(hoge **);//インスタンス生成用
};

//手続き用
int hoge::add(){
	x = -1;// xに-1を代入
	deletehoge(this);//自分自身のポインタを渡す
	return 0;
}

//廃棄
int hoge::deletehoge(hoge *a){
	if(a->x < 100){//xが100以下なので廃棄実効
		printf("delete完了\n");
		delete a;//廃棄
		a = NULL;//新たに生成するためNULLを代入しておく
	}
	return 0;
}

int hoge::newhoge(hoge *hg[]){

	for(int i = 0;i < 10;++i){
		if(hg[i] == NULL){//NULLで初期化されているポインタを探しインスタンスを生成する
						  //a = NULLでポインタを初期化したはずだがここのif文が実行されない
			printf("インスタンス生成\n");
			hg[i] = new hoge();
		}
	}
	return 0;
}

int main(){

	hoge *hg[10];//ここのポインタ配列を使いまわしたい

//インスタンス生成
	for(int i = 0;i < 10;++i){
		hg[i] = new hoge();
	}

//廃棄処理
	for(int i = 0;i < 10;++i){
		hg[i]->add();
	}

	hoge::newhoge(hg);//インスタンスを生成

	return 0;
}

ISLe
記事: 2648
登録日時: 9年前
連絡を取る:

Re: メンバ変数をキーに自分自身をdeleteしたい

#2

投稿記事 by ISLe » 7年前

intを関数で書き換えるにはintへのポインタを渡す必要があるように、hogeへのポインタを書き換えるにはhogeへのポインタへのポインタを渡す必要があります。
taketoshi さんが書きました:ステップ実行で追っていくときちんとNULLで初期化はされているようなのですが・・・。
NULLを代入しているのは、hoge::deletehoge関数の引数aに対してで、呼び出し元には影響しません。

thisポインタを書き換えるのもよろしくないと思います。
taketoshi さんが書きました:メンバ変数を監視し、ある特定条件を満たしたら自分自身をdeleteし
また後でそのポインタを使いまわすようにするにはどのような処理にすればよいでしょうか。
お望みの形とは違うかもしれませんが、先日ブログに投稿したソースコードが参考になるかもしれません。
タスクシステムを作ってみる 第9回: ISLeのビデオゲーム工房

ISLe
記事: 2648
登録日時: 9年前
連絡を取る:

Re: メンバ変数をキーに自分自身をdeleteしたい

#3

投稿記事 by ISLe » 7年前

そもそも使い回すのは、newで確保した領域か実体宣言した領域でないと意味がないのでは?

nil
記事: 428
登録日時: 8年前

Re: メンバ変数をキーに自分自身をdeleteしたい

#4

投稿記事 by nil » 7年前

thisポインタをdeleteしてはいけません。

私ならば、hogeクラスの上にhoge管理クラスをつくり、その中でメンバのhogeを監視し、
適切なタイミングでhogeをdeleteするようにします。

ISLe
記事: 2648
登録日時: 9年前
連絡を取る:

Re: メンバ変数をキーに自分自身をdeleteしたい

#5

投稿記事 by ISLe » 7年前

涼雅 さんが書きました:thisポインタをdeleteしてはいけません。
ええっ。これまでthisポインタをdeleteするコードをたくさん書いてしまいましたけど。
上で紹介したブログのコードでもやってますし。

delete thisしたあとthisを参照するコードが走らないようになってれば良いのでは。

nil
記事: 428
登録日時: 8年前

Re: メンバ変数をキーに自分自身をdeleteしたい

#6

投稿記事 by nil » 7年前

ISLe さんが書きました:
涼雅 さんが書きました:thisポインタをdeleteしてはいけません。
ええっ。これまでthisポインタをdeleteするコードをたくさん書いてしまいましたけど。
上で紹介したブログのコードでもやってますし。

delete thisしたあとthisを参照するコードが走らないようになってれば良いのでは。
すいません自分の思い違いのようでした。
訂正します

dic
記事: 582
登録日時: 9年前
住所: 宮崎県

Re: メンバ変数をキーに自分自身をdeleteしたい

#7

投稿記事 by dic » 7年前

http://www.kumei.ne.jp/c_lang/cpp/cpp_17.htm
当然のことながら、thisポインタはconstポインタですから、 これに、代入を行ってはいけません。

と、書いています。
私の方も確認したのですが、
deletehoge( this );
の前後で、thisポインタの値を見たのですが、一度 0x00000000 になったあと、
再び、有効な値に戻っています。

また、thisポインタを削除すると、thisポインタが指していた領域、hoge:add() が
どこのアドレスかわからなくなります。(スタックでわかりますが、その他の方法ではわからない)
deleteしているので、hoge::add() にもどったとき、すでにその領域はdeleteされている
という矛盾が発生します。
そしてさらに、deletehoge( this ); は const hoge* のようです。


ポインタ配列を使いまわしたい ということですので、
delete hg[0];
hg[0] = NULL;
とすればいいのでしょうけど、おそらく、 hg->add(); の関数の中で行いたいのですよね?

あと、32行目は

コード:

    for(int i = 0;i < 10; i++ ){
で、後方インクリメントにしてください。オーバーしてアクセスします。[/s]
私の勘違いでした

dic
記事: 582
登録日時: 9年前
住所: 宮崎県

Re: メンバ変数をキーに自分自身をdeleteしたい

#8

投稿記事 by dic » 7年前

コード:

// ネット質問1.cpp : コンソール アプリケーションのエントリ ポイントを定義します。
//

#include "stdafx.h"


#include<windows.h>
#include<stdio.h>


class	A;
void	DeleteA( A *hoge );
class	A
{
	int	x;
public:
	A()
	{
		x = 100;
	}
	~A()
	{
		printf( "Aのデストラクタ\n" );
		x = -100;
	}
	void	Print()
	{
		printf( "x = %d\n", x );
	}
	void	DelA()
	{
		x = 200;
		DeleteA( this );
		x = 400;	//	どこのxかわからない しかしここはAオブジェクトの中
					//	thisポインタの先がここ、しかしDeleteA関数で消しているここはどこ?
	}
};
void	DeleteA( A *hoge )
{
	delete hoge;
	hoge = 0;
}

int main(){

	A	*a[10];
	int	i;
	for( i=0; i<10; i++ )
		a[i] = new A();
	a[0]->DelA();
	a[0]->Print();

	for( i=0; i<10; i++ )
	{
		delete a[i];
		a[i] = NULL;
	}
	a[0]->Print();
	
    return 0;
}
検証用のコードを用意してみました
上のコードを実行すると、

Aのデストラクタ
x = 400

と期待したとおりには動きませんでした
期待したことは、エラーで止まることです。
デストラクタが呼ばれていてて、deleteが呼ばれているはずですが、次のステップが実行できてしまいます。


そして

コード:

// ネット質問1.cpp : コンソール アプリケーションのエントリ ポイントを定義します。
//

#include "stdafx.h"


#include<windows.h>
#include<stdio.h>


class	A;
void	DeleteA( A *hoge );
class	A
{
	int	x;
public:
	A()
	{
		x = 100;
	}
	~A()
	{
		printf( "Aのデストラクタ\n" );
		x = -100;
	}
	void	Print()
	{
		printf( "x = %d\n", x );
	}
	void	DelA()
	{
		x = 200;
		DeleteA( this );
		x = 400;	//	どこのxかわからない しかしここはAオブジェクトの中
					//	thisポインタの先がここ、しかしDeleteA関数で消しているここはどこ?
	}
};
void	DeleteA( A *hoge )
{
	delete hoge;
	hoge = 0;
}

int main(){

	A	*a[10];
	int	i;
	for( i=0; i<10; i++ )
		a[i] = new A();
//	a[0]->DelA();
//	a[0]->Print();

	for( i=0; i<10; i++ )
	{
		delete a[i];
		a[i] = NULL;
	}
	a[0]->Print();
	
    return 0;
}

として、コメントアウト後、実行すると
a[0]->Print(); のところで、実行エラーが起きました。

最後に、 a = NULL; をコメントアウトすると
a[0]->Print(); で

x = -123423121(適当)

と、deleteされていることが確認できました。

ISLe
記事: 2648
登録日時: 9年前
連絡を取る:

Re: メンバ変数をキーに自分自身をdeleteしたい

#9

投稿記事 by ISLe » 7年前

最初の返信に書いたように、ポインタ変数の内容を書き換えるには、ポインタ変数へのポインタを使わなければいけませんから、仮にthisポインタを書き換えることができたとしても、それを格納している変数の内容を書き換えることにはなりません。


わたしも詳しいわけではないですが、気になる記述なので指摘させていただきます。
dic さんが書きました:私の方も確認したのですが、
deletehoge( this );
の前後で、thisポインタの値を見たのですが、一度 0x00000000 になったあと、
再び、有効な値に戻っています。
thisポインタは言わばメンバ関数呼び出しの際の隠し引数です。
なのでメンバ関数が呼び出されるたびに設定されます。
dic さんが書きました:また、thisポインタを削除すると、thisポインタが指していた領域、hoge:add() が
どこのアドレスかわからなくなります。(スタックでわかりますが、その他の方法ではわからない)
deleteしているので、hoge::add() にもどったとき、すでにその領域はdeleteされている
という矛盾が発生します。
解体されるか上書きされることで、thisポインタというかthisポインタが指すオブジェクトは無効になります。
無効になったオブジェクトに対して非静的メンバ変数のアクセスや非静的メンバ関数の呼び出しを行うことは未定義の動作となります。
それに該当しなければ、メンバ関数内で解体したり上書きすることも、認められています。
dic さんが書きました:と期待したとおりには動きませんでした
期待したことは、エラーで止まることです。
デストラクタが呼ばれていてて、deleteが呼ばれているはずですが、次のステップが実行できてしまいます。
上に書いたように無効なオブジェクトの非静的メンバ関数を呼び出したり非静的メンバ変数にアクセスすることは未定義の動作です。
期待通りに動作してしまう場合があることも未定義の動作に含まれます。
dic さんが書きました:として、コメントアウト後、実行すると
a[0]->Print(); のところで、実行エラーが起きました。
未定義の動作なので期待通り動作する場合があるかもしれません。
dic さんが書きました:最後に、 a = NULL; をコメントアウトすると
a[0]->Print(); で

x = -123423121(適当)

と、deleteされていることが確認できました。

未定義の動作なので期待通り動作する場合があるかもしれません。

taketoshi
記事: 221
登録日時: 9年前
住所: 日本国

Re: メンバ変数をキーに自分自身をdeleteしたい

#10

投稿記事 by taketoshi » 7年前

みなさんアドバイスありがとうございます。

アドバイスいただいた後、デバックしたりメモリのアドレスを表示したりして考えました。
メンバ変数をキー値として、自分自身をdeleteし、そして新たにインスタンスを生成という手順を
同じポインタ配列を使いまわし、要素数は一番小さい場所を使うようにNull値を代入したかったです。
thisポインタがconstだったのにdeleteできちゃうところが非常に疑問でした。
おおよそdicさんがアドバイスしていただいた動作をしていたのだと思っております。

メンバ関数内で必ずしもdeleteすることにはこだわっておらず
thisポインタをreturn出来ると教えていただいたので以下のように動作を書き直しました。
► スポイラーを表示
上位にタスククラスを作らないとこのままでは使えそうにありませんが
敵とか弾をnewし、HPが0になったらdelete、そして再度newするという動作がこれで書けそうです。

一応解決とさせていただきます。
アドバイスありがとうございます。

ISLe
記事: 2648
登録日時: 9年前
連絡を取る:

Re: メンバ変数をキーに自分自身をdeleteしたい

#11

投稿記事 by ISLe » 7年前

deleteしたくないときに0を返したら、解体されないままhgがNULLになってしまいますけど。
taketoshi さんが書きました:上位にタスククラスを作らないとこのままでは使えそうにありませんが
敵とか弾をnewし、HPが0になったらdelete、そして再度newするという動作がこれで書けそうです。

やりたいことがメモリ領域の使い回しではなくて、自動的にdeleteしたいということでしたらスマートポインタを使うべきかと。
スマートポインタを使うとmain関数から抜けてスマートポインタ自体が解体されるときにも解体してくれますよ。

taketoshi
記事: 221
登録日時: 9年前
住所: 日本国

Re: メンバ変数をキーに自分自身をdeleteしたい

#12

投稿記事 by taketoshi » 7年前

やりたいことは、スコープを抜けての自動deleteではなく
DXライブラリの中のwhile文で回っているスコープ内での敵クラス等の管理になります。

たとえば、そのなかで宣言している敵クラスへのポインタ変数を

コード:

//例

#define enemymax 100

enemy *en[enemymax];
のように予め登場する敵の最大数へのポインタを宣言しておき
20体の敵がプログラム上に登場しているときは敵ポインタen[20]までの要素を使用している

たとえばen[15]の要素を持つ、敵のHPが0になった場合はen[15]をdeleteしNULLを代入し次の敵が出現するタイミングを待ちます
次の敵が出現するタイミングになったらen[enemymax]の要素数の中でNULLになっている要素、即ちen[15]に対して敵クラスをnewし
敵を出現させてるプログラムを組みたいと思っています。

ゲームを開発している最中ですので、これ以外にもこういった方法がある、というのがあればアドバイスいただきたいです。

アバター
h2so5
副管理人
記事: 2212
登録日時: 9年前
住所: 東京
連絡を取る:

Re: メンバ変数をキーに自分自身をdeleteしたい

#13

投稿記事 by h2so5 » 7年前

thisを返す必要性が見当たりません。
このような実装で十分では?

コード:

bool hoge::is_terminated() {
    x = -1;// xに-1を代入
    return (x < 0);
}

コード:

    //削除フェーズ
    for(int i = 0;i < 10;++i){
        if(hg[i] && hg[i]->is_terminated()){//戻り値をチェックする0以外ならば削除対象とする
            delete hg[i];//deleteを実行する
            printf("delete完了\n");
            hg[i] = nullptr;//次に使いまわす為、キー値としてNULLを代入しておく
        }
    }
※もちろん、スマートポインタを使うのがbetterだと思いますが。

ISLe
記事: 2648
登録日時: 9年前
連絡を取る:

Re: メンバ変数をキーに自分自身をdeleteしたい

#14

投稿記事 by ISLe » 7年前

taketoshi さんが書きました:やりたいことは、スコープを抜けての自動deleteではなく
DXライブラリの中のwhile文で回っているスコープ内での敵クラス等の管理になります。
わたしがブログで公開していると紹介したコードは見てもらえました?

ゲームプログラムで使いやすいように、スマートポインタは生のポインタに近い使い方になってます。
ふつうにヌルポインタを代入すれば参照が外れるようになってますけど。
taketoshi さんが書きました:たとえばen[15]の要素を持つ、敵のHPが0になった場合はen[15]をdeleteしNULLを代入し次の敵が出現するタイミングを待ちます
次の敵が出現するタイミングになったらen[enemymax]の要素数の中でNULLになっている要素、即ちen[15]に対して敵クラスをnewし
敵を出現させてるプログラムを組みたいと思っています。
ふつうにnew/deleteするのでかまわないのならstd::listとか使えば良いのではないでしょうか。
配列から要素を探す手間が省けます。

ISLe
記事: 2648
登録日時: 9年前
連絡を取る:

Re: メンバ変数をキーに自分自身をdeleteしたい

#15

投稿記事 by ISLe » 7年前

こういうことではないのでしょうかね。

コード:

#include <iostream>
#include <list>
#include <memory>

class hoge {
private:
	int x;
public:
	bool check();
};
 
bool hoge::check() {
	x = -1;
	return (x < 0);
}
 
int main()
{
	const int ENEMYMAX = 100;

	typedef std::shared_ptr<hoge> hoge_ptr;
	typedef std::list<hoge_ptr> hglist;
	hglist hg;

	// 要素がENEMYMAX個に満たないならENEMYMAX個まで増やす
	while (hg.size() < ENEMYMAX) {
		hg.push_back(hoge_ptr(new hoge()));
	}

	hglist::iterator it = hg.begin();
	while (it != hg.end()) {
		if ((*it)->check()) {
			it = hg.erase(it); // 要素を削除
		}
		else {
			++it;
		}
	}

	// 要素がENEMYMAX個に満たないならENEMYMAX個まで増やす
	while (hg.size() < ENEMYMAX) {
		hg.push_back(hoge_ptr(new hoge()));
	}

	return 0;
}

taketoshi
記事: 221
登録日時: 9年前
住所: 日本国

Re: メンバ変数をキーに自分自身をdeleteしたい

#16

投稿記事 by taketoshi » 7年前

>>ISLeさん

ソースコードのご提示ありがとうございます。
VS2008SP1を入れていなかったため、
shard_ptrを使用する環境の構築に時間がかかってしまいました。

提示いただいたブログは読ませていただきましたが
クラスの継承関係などの知識が不足していたため、十分に理解できませんでした。

スマートポインタを使った事がないのですが、後から提示いただいた
shard_ptrのコードは理解が出来そうです。

コンテナのlistはメモリの確保必要ないですし、pushbackで要素の一番最後に代入できるので、
空きスペースを探すためわざわざnullポインタを代入する必要もないと思いました。

今作ってるゲームのキャラクター管理にこの概念を取り入れたいと思います。
ありがとうございます。

taketoshi
記事: 221
登録日時: 9年前
住所: 日本国

Re: メンバ変数をキーに自分自身をdeleteしたい

#17

投稿記事 by taketoshi » 7年前

もう一点スマートポインタの使い方についてご教示お願いします。
下記のようなコードを書いたのですが、スマートポインタに代入する際なぜ 型名(new int())の記述なのでしょう。
スマートポインタ変数 = new int();ではだめでした。スマートポインタがどの様に動いているのか教えていただけると有難いです。

コード:

#include "stdafx.h"
#include<memory>

using namespace std;
using namespace std::tr1;

int _tmain(int argc, _TCHAR* argv[])
{

	//型を宣言する
	typedef shared_ptr<int> s_int_ptr;

	//スマートポインタの変数aにs_int_ptrを・・・何してるんだろう?
	//なぜ 型名(new int())の記述?
	//s_int_ptr = a = new int()ではないのか?
	s_int_ptr a = s_int_ptr(new int());

	*a = 100;

	printf("%d",*a);
	return 0;
}


*********追記**********

コードを書いて1ステップずつ頭の中で整理していきました。
コンストラクタがオーバーロードされていて、shared_ptr(型)とすることでスマートポインタへのポインタを返しているみたいですね。
自己解決いたしました。

ISLe
記事: 2648
登録日時: 9年前
連絡を取る:

Re: メンバ変数をキーに自分自身をdeleteしたい

#18

投稿記事 by ISLe » 7年前

エラーになるのはコンストラクタで暗黙の型変換が禁止されているためです。
C++スタイルの初期化を使ってください。

s_int_ptr a(new int());

taketoshi
記事: 221
登録日時: 9年前
住所: 日本国

Re: メンバ変数をキーに自分自身をdeleteしたい

#19

投稿記事 by taketoshi » 7年前

>>ISLeさん

explicitという宣言がshared_ptrのコンストラクタについていました。
之が暗黙の型変換の禁止なんですね。ありがとうございます。

閉鎖

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