C++ での Singleton クラスの実装

黄舟
黄舟オリジナル
2016-12-12 17:42:301151ブラウズ

「デザインパターン」では、シングルトンはリターンポインタとして記述されています:

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

対応する実装 cpp ファイルは:

Singleton* Singleton::_instance;
Singleton* Singleton::Instance(){
    if( _instance == 0){
        _instance = new Singleton;
    };
    return _instance;
}

コンストラクタを保護されたものとして設計する目的は、クラス外での新規作成を防ぐためです。 private を考慮すると、このクラスを継承できる場合は、コンストラクターを protected として設計し、仮想デストラクターを追加する必要があります。他の人が Singleton オブジェクトをコピーできないようにするには:

Singleton* pSingleton = Singleton::Instance();
Singleton s1 = *pSingleton;
Singleton s2 = *pSingleton; 
需要将拷贝构造(copy constructor)函数变成 private。

しかし、ここでの問題は、Singleton オブジェクトをいつ削除するかということです。 C++ の基本原則に従って、オブジェクトは作成された場所では必ず破棄されます。Singleton オブジェクトを削除するための destroy メソッドも必要です。消し忘れるとさらに面倒です。インスタンス関数には、複数のスレッドによる同時アクセスによるロックの問題もあります。インスタンス関数の最初と最後にロックとロック解除を配置すると、関数全体のパフォーマンスが大幅に低下します。これは良い設計ではありません。
Singleton オブジェクトの削除忘れによって引き起こされるメモリ リークの問題を回避できる小さな変更があります。つまり、 std:auto_ptr を使用して Singleton オブジェクトを含め、クラスの静的メンバー auto_ptr オブジェクトを定義し、静的 auto_ptr 変数が破棄されたときに Singleton オブジェクトを自動的に削除します。ユーザーがシングルトン オブジェクトを削除できないようにするには、デストラクターをパブリックからプロテクトに変更する必要があります。以下は、ヘッダー ファイル SingletonAutoPtr.h です:

#include <memory>
using namespace std;
class CSingletonAutoPtr 
{
private:
    static auto_ptr<CSingletonAutoPtr> m_auto_ptr;
    static CSingletonAutoPtr* m_instance;
protected:
    CSingletonAutoPtr();
    CSingletonAutoPtr(const CSingletonAutoPtr&);
    virtual ~CSingletonAutoPtr(); 
    //allow auto_ptr to delete, using protected ~CSingletonAutoPtr()
    friend class auto_ptr<CSingletonAutoPtr>; 
public:
    static CSingletonAutoPtr* GetInstance();
    void Test();
};

#p# は、次の SingletonAutoPtr.cpp に対応します:

#include "SingletonAutoPtr.h"
#include <iostream>
//initial static member vars here 
CSingletonAutoPtr* CSingletonAutoPtr::m_instance = NULL;
auto_ptr<CSingletonAutoPtr> CSingletonAutoPtr::m_auto_ptr;
/////////////////////////////////////////
// Construction/Destruction
/////////////////////////////////////////
CSingletonAutoPtr::CSingletonAutoPtr()
{
    cout << "CSingletonAutoPtr::CSingletonAutoPtr()" << endl;
    //put single object into auto_ptr object 
    m_auto_ptr = auto_ptr<CSingletonAutoPtr>(this);
}
CSingletonAutoPtr::~CSingletonAutoPtr()
{
    cout << "CSingletonAutoPtr::~CSingletonAutoPtr()" << endl;
}
CSingletonAutoPtr* CSingletonAutoPtr::GetInstance()
{
    //begin lock
    //....
    if(m_instance == NULL)
        m_instance = new CSingletonAutoPtr();
    //end lock
    //...
    return m_instance;
    }
void CSingletonAutoPtr::Test()
{
    cout << "CSingletonAutoPtr::Test()" << endl;
}

メソッドの呼び出し:

CSingletonAutoPtr* pSingleton = CSingletonAutoPtr::GetInstance();
pSingleton->Test();

C++ でシングルトンを記述するには、予想を超える多大な労力が必要です。 auto_ptr を使用したことがない人も多く、std:auto_ptr 自体はオブジェクト所有権メカニズムに基づいています。これとは対照的に、Apache Log4cxx にはオブジェクトのカウントに基づいた auto_ptr があります。 。適切な auto_ptr を使用するためだけに log4cxx を使用しなければならないのは、多くのプロジェクトにとって良いことではありません。もちろん、上記の例を記述するには、ANSI C++ の STL の std:auto_ptr で十分です。

#p#もう 1 つのアイデアは、GetInstance 関数を静的メンバーとして設計する方がよいということです。一般的に言えば、Singleton オブジェクトは大きくないためです。静的メンバーは常にメモリを占有する必要がありますが、それは大きな問題ではありません。ここでのデストラクターは public に設定する必要があります。以下はヘッダーファイルです。SingleStaticObj.h

class CSingletonStaticObj 
{
private:
    static CSingletonStaticObj m_instance;
protected:
    CSingletonStaticObj();
    CSingletonStaticObj(const CSingletonStaticObj&);
public:
    virtual ~CSingletonStaticObj(); //must public
    static CSingletonStaticObj& GetInstance();
    void Test();
};
    对应的 SingleStaticObj.cpp 文件为:
#include "SingletonStaticObj.h"
#include <string>
#include <iostream>

using namespace std;
CSingletonStaticObj CSingletonStaticObj::m_instance;
CSingletonStaticObj::CSingletonStaticObj()
{
    cout << "CSingletonStaticObj::CSingletonStaticObj()" << endl;
}
CSingletonStaticObj::~CSingletonStaticObj()
{
    cout << "CSingletonStaticObj::~CSingletonStaticObj()" << endl;
}
CSingletonStaticObj& CSingletonStaticObj::GetInstance()
{
    return m_instance;
}
void CSingletonStaticObj::Test()
{
    cout << "CSingletonStaticObj::Test()" << endl;
}

呼び出しメソッド:

CSingletonStaticObj& singleton = CSingletonAutoPtr::GetInstance();singleton.Test();

コードサイズの観点から、静的メンバー ref を使用した方が簡単なようです。私はこの方法の方が好きです。

ただし、静的メンバー シングルトンはすべての状況に適しているわけではありません。たとえば、GetInstance は、異なるインスタンスを返すことを動的に決定する必要がある場合には使用できません。たとえば、FileSystem::GetInstance は、Windows で実行する場合は新しい WinFileSystem を返す必要があり、Linux/Unix で実行する場合は新しい LinuxFileSystem を返す必要がある場合があります。この場合でも、シングルトン ポインターを含む上記の auto_ptr メソッドを使用する必要があります。


声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。