ページ 1 / 1
シングルトンパターンのインスタンスの削除方法
Posted: 2009年6月30日(火) 13:48
by dic
今下記までの骨組みはできたのですが
インスタンスを生成してますが、削除方法がどうやったらいいのかわかりません
通常だったら、デストラクタに 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;
}
Re:シングルトンパターンのインスタンスの削除方法
Posted: 2009年6月30日(火) 15:48
by GPGA
普通シングルトンクラスのインスタンスは静的領域を使用するので、deleteを使う必要はないかと
class Singleton {
public :
static Singleton* Instance();
};
Singleton* Singleton::Instance() {
static Singleton ins;
return &ins;
}
Re:シングルトンパターンのインスタンスの削除方法
Posted: 2009年6月30日(火) 22:25
by Justy
>デストラクタに delete _instance とすべきなのでしょうか?
途中で明示的に破棄する必要があるなら、 Destory()とかの静的メソッドとかを作って、
その中で deleteすればいいのではないでしょうか。
終了までずっと存在し続けるなら、std::atexitで破棄関数を登録して、その中で deleteする
という手があります。
ただ、どちらにしてもその際に _instanceを NULLで初期化するなら
それはデストラクタでやったほうがいいです。
ところで、コンストラクタが protectedになっていますが、このクラスは継承する予定が??
Re:シングルトンパターンのインスタンスの削除方法
Posted: 2009年7月01日(水) 09:31
by dic
>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:シングルトンパターンのインスタンスの削除方法
Posted: 2009年7月01日(水) 10:33
by ねこ
多分終了時のメモリリークの警告の事ですよね。
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関数の中、修正しました。
Re:シングルトンパターンのインスタンスの削除方法
Posted: 2009年7月01日(水) 11:01
by GPGA
>たしかに静的領域なので、変数の確保はされてるんですが、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; // プログラム終了後、シングルトンインスタンスが解放される
}
Re:シングルトンパターンのインスタンスの削除方法
Posted: 2009年7月01日(水) 13:25
by dic
>>ねこさん、GPGAさん
丁寧な回答ありがとうございます
なるほど、そういうことだったんですね
ようやく理解できました
ありがとうございました
Re:シングルトンパターンのインスタンスの削除方法
Posted: 2009年7月01日(水) 14:29
by dic
>>GPGAさん
確認したいのですが
画像のようにヒープ領域は、newで利用され、deleteが必要で
static変数などは、exeファイルにはじめから格納されており
exeファイルを読み込んだ時点で、すでにstatic変数は確保されてるということでしょうか?
Re:シングルトンパターンのインスタンスの削除方法
Posted: 2009年7月01日(水) 15:26
by GPGA
>exeファイルを読み込んだ時点で、すでにstatic変数は確保されてるということでしょうか?
exeファイルを実行すると、まずプログラム領域が確保され、そこにexeファイルを展開します。
次に、スタック領域、静的領域が確保され、あまった領域がヒープ領域となります。
従って、exeを実行した時点でメモリは確保されますが、実態化する(コンストラクタが呼ばれる)のは
静的グローバル変数(関数外のstatic変数)は、exeが実行されたときで
静的ローカル変数(関数内のstatic変数)は、その変数が定義されている位置に処理が来たときです。
Re:シングルトンパターンのインスタンスの削除方法
Posted: 2009年7月01日(水) 17:38
by dic
なるほど
よくわかりました
ありがとうございました
Re:シングルトンパターンのインスタンスの削除方法
Posted: 2009年7月01日(水) 23:19
by Justy
もう解決していますが、ひとつだけ。
この手のシングルトンの設計は寿命の問題が厄介になります。
ポインタとして割り付けた場合はわりと制御しやすいのですが、
静的領域においた場合、解放は終了時に自動で行われてしまい、
そのままではプログラマがタイミングの制御ができません。
大抵の場合それでも問題はないとは思いますが、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クラスにアクセスすることになり、未定義の動作となります。
まぁ、そんな使い方しなければいいだけの話かもしれませんが。