シングルトンパターンのインスタンスの削除方法

フォーラム(掲示板)ルール
フォーラム(掲示板)ルールはこちら  ※コードを貼り付ける場合は [code][/code] で囲って下さい。詳しくはこちら
dic

シングルトンパターンのインスタンスの削除方法

#1

投稿記事 by dic » 16年前

今下記までの骨組みはできたのですが
インスタンスを生成してますが、削除方法がどうやったらいいのかわかりません

通常だったら、デストラクタに delete _instance とすべきなのでしょうか?
どこでdelete _instance とすれば適切でしょうか
#include	<stdio.h>

class	Singleton
{
private:
	static	Singleton*	_instance;
protected:
	Singleton();
public:
	static	Singleton*	Instance();
	void	TestShow();
};
//------------------------------------------------------
Singleton*	Singleton::_instance = 0;
Singleton*	Singleton::Instance() {
	if( _instance == 0 ) {
		_instance = new Singleton;
		printf( "create instance.\n" );
	}
	return _instance;
}
Singleton::Singleton(){
}
void	Singleton::TestShow(){
	printf( "TestShow called.\n" );
}

//------------------------------------------------------
int		main()
{
	Singleton::Instance()->TestShow();

	return 0;
}

GPGA

Re:シングルトンパターンのインスタンスの削除方法

#2

投稿記事 by GPGA » 16年前

普通シングルトンクラスのインスタンスは静的領域を使用するので、deleteを使う必要はないかと

class Singleton {
public :
	static Singleton* Instance();
};

Singleton* Singleton::Instance() {
	static Singleton ins;
	return &ins;
}
 

Justy

Re:シングルトンパターンのインスタンスの削除方法

#3

投稿記事 by Justy » 16年前


>デストラクタに delete _instance とすべきなのでしょうか?

 途中で明示的に破棄する必要があるなら、 Destory()とかの静的メソッドとかを作って、
その中で deleteすればいいのではないでしょうか。

 終了までずっと存在し続けるなら、std::atexitで破棄関数を登録して、その中で deleteする
という手があります。


 ただ、どちらにしてもその際に _instanceを NULLで初期化するなら
それはデストラクタでやったほうがいいです。


 ところで、コンストラクタが protectedになっていますが、このクラスは継承する予定が??

dic

Re:シングルトンパターンのインスタンスの削除方法

#4

投稿記事 by dic » 16年前

>GPGAさん
ちょっと改造して下記のようにしましたところ
newが開放されてないと警告がでました

デスクトップ\singleton\main.cpp(25) : {39} normal block at 0x004301D0, 1 bytes long.
#include	<stdio.h>
#define _CRTDBG_MAP_ALLOC
#include <crtdbg.h>
#ifdef _DEBUG
#define new new( _NORMAL_BLOCK, __FILE__, __LINE__ )
#endif


class	Singleton
{
private:
	static	Singleton*	_instance;
protected:
	Singleton();
public:
	static	Singleton*	Instance();
	void	TestShow();
	void	Delete();
};
//----------------------------------------------------
Singleton*	Singleton::_instance = 0;
Singleton*	Singleton::Instance() {
	if( _instance == 0 ) {
		_instance = new Singleton;
		printf( "create instance.\n" );
	}
	return _instance;
}
Singleton::Singleton(){
}
void	Singleton::Delete(){
	delete _instance;
}
void	Singleton::TestShow(){
	printf( "TestShow called.\n" );
}
//----------------------------------------------------
int		main()
{
	_CrtSetDbgFlag( _CRTDBG_LEAK_CHECK_DF | _CRTDBG_ALLOC_MEM_DF );

	Singleton::Instance()->TestShow();

	//	delete を呼ばない
//	Singleton::Instance()->Delete();

	return 0;
}
たしかに静的領域なので、変数の確保はされてるんですが、delete しなくて大丈夫なのでしょうか?

>>Justyさん
最初から最後まで存在するんで、デストラクタを検討したいと思います

継承は練習で、するつもりです
ただ、実戦では頭がこんがらがるのでしないと思います

ねこ

Re:シングルトンパターンのインスタンスの削除方法

#5

投稿記事 by ねこ » 16年前

多分終了時のメモリリークの警告の事ですよね。
Justyさんが書かれている「atexit」関数に削除関数を追加。
それからDelete関数はNULLで無い場合に処理するように作成。
class A
{
private:
	static A* m_pA;
protected:
	A(){
		int a = 0;	// ブレイクポイント用
	}
public:
	virtual ~A(){
	}

	// 削除
	static void Delete()
	{
		if( m_pA != NULL )
			delete m_pA;
		m_pA = NULL;
	}

	// 生成取得
	static A* Instance()
	{
		if( m_pA == NULL )
			m_pA = new A();
		return m_pA;
	}
};
// 静的変数初期化
A* A::m_pA = NULL;

// 初期化フラグ
BOOL g_bInit = FALSE;

void main()
{
	// 初期化
	if( g_bInit == FALSE )
	{
		// 終了時に呼び出す関数の登録
		atexit( A::Delete );
		
		// インスタンス生成
		A::Instance();

		// 消す⇒消さないに変更
		//A::Delete();

		// 初期化フラグON
		g_bInit = TRUE;
	}
}
#Instance関数の中、修正しました。

GPGA

Re:シングルトンパターンのインスタンスの削除方法

#6

投稿記事 by GPGA » 16年前

>たしかに静的領域なので、変数の確保はされてるんですが、delete しなくて大丈夫なのでしょうか?
newで確保する領域はヒープ領域です。
静的領域は、グローバル変数やstatic変数を確保する領域のことです。

最初から最後までいるわけですから、new/deleteを使用せずstatic変数にすればいいと思うわけです。
class Singleton {
	Singleton(){}
public :
	static Singleton* Instance();
	void Func(){}
};

Singleton* Singleton::Instance() {
	static Singleton ins;	// これはエラーにならない
	return &ins;
}

int main() {
	Singleton ins; // エラーになる
	Singleton* p = new Singleton(); // エラーになる
	Singleton::Instance()->Func(); // エラーにならない
	return 0; // プログラム終了後、シングルトンインスタンスが解放される
}
 

dic

Re:シングルトンパターンのインスタンスの削除方法

#7

投稿記事 by dic » 16年前

>>ねこさん、GPGAさん
丁寧な回答ありがとうございます
なるほど、そういうことだったんですね
ようやく理解できました
ありがとうございました

dic

Re:シングルトンパターンのインスタンスの削除方法

#8

投稿記事 by dic » 16年前

>>GPGAさん
確認したいのですが
画像のようにヒープ領域は、newで利用され、deleteが必要で
static変数などは、exeファイルにはじめから格納されており
exeファイルを読み込んだ時点で、すでにstatic変数は確保されてるということでしょうか?

GPGA

Re:シングルトンパターンのインスタンスの削除方法

#9

投稿記事 by GPGA » 16年前

>exeファイルを読み込んだ時点で、すでにstatic変数は確保されてるということでしょうか?
exeファイルを実行すると、まずプログラム領域が確保され、そこにexeファイルを展開します。
次に、スタック領域、静的領域が確保され、あまった領域がヒープ領域となります。
従って、exeを実行した時点でメモリは確保されますが、実態化する(コンストラクタが呼ばれる)のは
静的グローバル変数(関数外のstatic変数)は、exeが実行されたときで
静的ローカル変数(関数内のstatic変数)は、その変数が定義されている位置に処理が来たときです。

dic

Re:シングルトンパターンのインスタンスの削除方法

#10

投稿記事 by dic » 16年前

なるほど
よくわかりました
ありがとうございました

Justy

Re:シングルトンパターンのインスタンスの削除方法

#11

投稿記事 by Justy » 16年前

 もう解決していますが、ひとつだけ。
 
 この手のシングルトンの設計は寿命の問題が厄介になります。
 
 ポインタとして割り付けた場合はわりと制御しやすいのですが、
静的領域においた場合、解放は終了時に自動で行われてしまい、
そのままではプログラマがタイミングの制御ができません。

 大抵の場合それでも問題はないとは思いますが、2つの静的領域に配置された
オブジェクトがあり、一方のデストラクタで一方を呼び出していたりすると
破棄される順によっては既に破棄済みのオブジェクトにアクセスすることに
なります。

 例えば、これ。
[color=#d0d0ff" face="monospace]
#include <iostream>
#include <string>

class Log
{
Log()
{
ostream_ = &std::cout;
std::cout << "Log::Constractor" << std::endl;
}
~Log()
{
ostream_ = 0;
std::cout << "Log::Destructor" << std::endl;
}
std::ostream * ostream_;


Log(const Log&);
Log& operator=(const Log&);

public:

static Log& Instance()
{
static Log s;
return s;
}

void Func(const std::string &message)
{
*ostream_ << message << std::endl;
}
};

class Singleton
{
Singleton() { }
~Singleton() { Log::Instance().Func("Singleton::Destructor"); }

public:

void Func() { Log::Instance().Func("Singleton::Func"); }

static Singleton& Instance()
{
static Singleton s;
return s;
}
};

int main()
{
Singleton::Instance().Func();
return 0;
}
[/color]

 Singletonクラスのデストラクタよりも Logクラスのデストラクタの方が先に走るので、
破棄された Logクラスにアクセスすることになり、未定義の動作となります。

 まぁ、そんな使い方しなければいいだけの話かもしれませんが。

閉鎖

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