2010年4月25日日曜日

C++でSingletonパターン

Singletonパターンはクラスのインスタンスが一つしかないことを保証するパターンです。
Singletonパターンのメリットは
  • 不要なインスタンスの生成を禁止することができる。
です。
まずは次のクラス図をご覧ください

このクラス図で重要なのは
  • getInstance()メソッド(static Singleton& getInstance())
  • privateなコンストラクタ(Singleton)
の二つですこれらによりsingletonを実現します。
つぎにソースを見てみましょう、用意するのは次のファイルです。
  • main.cpp
  • Singleton.h
  • Singleton.cpp
ますはSingleton.hです。
  1. #ifndef _SINGLETON_H_  
  2. #define _SINGLETON_H_  
  3.   
  4. class Singleton  
  5. {  
  6. public:  
  7.   
  8. // get instance  
  9. static Singleton& getInstance(); // ①  
  10.   
  11. // to string  
  12. const char*const toString(); // ②  
  13.   
  14. // destructor  
  15. ~Singleton(); // ③  
  16.   
  17. private:  
  18. // constructor  
  19. Singleton(); // ④  
  20. Singleton(const Singleton& _singleton); // ⑤  
  21.   
  22. // initialize  
  23. const bool init(); // ⑥  
  24.   
  25. // operator(=)  
  26. Singleton& operator=(const Singleton& _singleton); // ⑦  
  27.   
  28. };  
  29. #endif  

①唯一のインスタンスを取得するためのインタフェースです。
②文字列化のインタフェースです。Singletonパターンとは関係有りません。
③デストラクタです。
④⑤インスタンスの生成を禁止するためにコンストラクタをprivateで宣言しています。
⑥初期化メソッドですSingletonパターンとは関係ありません。
⑦operator=をprivateで宣言することによりインスタンスのコピーを禁止しています。Singletonパターンとは関係有りません。

次にSingleton.cppファイルを見てみます。
  1. #include "Singleton.h"  
  2.   
  3. Singleton& Singleton::getInstance()  
  4. {  
  5. static Singleton singleton; // ①  
  6.   
  7. static bool isAlreadyInit = false// ②  
  8. if (isAlreadyInit) {  
  9. // do nothing...  
  10. }  
  11. else {  
  12. isAlreadyInit = singleton.init();  
  13. }  
  14. return singleton;  
  15. }  
  16.   
  17. // to string  
  18. const char*const Singleton::toString()  
  19. {  
  20. static char* str = "singleton";  
  21. return str;  
  22. }  
  23.   
  24. // constructor  
  25. Singleton::Singleton()  
  26. {  
  27. // do nothing...  
  28. }  
  29.   
  30. // destructor  
  31. Singleton::~Singleton()  
  32. {  
  33. // do nothing...  
  34. }  
  35.   
  36. // initialize  
  37. const bool Singleton::init()  
  38. {  
  39. // do nothing...  
  40. return true;  
  41. }  

①唯一のインスタンスをstaticで定義しています。
②初期化処理を最初の1回だけ行うために初期化済みフラグを定義して初期化したらtrueを代入します。
注:この実装はスレッドセーフではありません。

最後にmain.cppです。

  1. #include "Singleton.h"  
  2. #include <stdio.h>  
  3.   
  4. void main (void)  
  5. {  
  6. Singleton& apple = Singleton::getInstance();    // ①  
  7. printf("%s\n", apple.toString());       // ②  
  8. printf("hit any key...\n");             // ③  
  9. while(getchar() == EOF);                // ④  
  10. }  
  11. </stdio.h>  

①getInstance()メソッドでインスタンスを取得しています。
②取得したインスタンスのtoString()メソッドを呼び出しています。
③"hit any key..."を標準出力に表示しています。
④いずれかのkeyが押されるまでループしています。

以上でSingletonパターンの説明は終わりです。ソースコードはコピペで動かせるハズですので是非試してみてください。

2 件のコメント:

  1. こんにちは、いつも読ませていただいてます。
    二点指摘させていただきます。

    一つ目は、Singleton.cppの

    bool isAlreadyInit = false;

    にstaticをつけるべきだと思います。これをつけないと、getInstanceが呼ばれる度にinitされてしまいます。

    二つ目はそもそもinitがいらないということです。どういう事かといいますと、

    static Singleton singleton;

    と書くと、一回目のgetInit呼び出しにおいてのみSingletonのコンストラクタが呼ばれるので、初期化を明示的に書く必要はありません。

    参考までにコードを書いてみました。

    http://ideone.com/lYtQK

    返信削除
  2. コメントありがとうございます。(約一週間放置orz
    コードの指摘より初めてコメントがついてうれしいです。
    bool isAlreadyInit = false;
    は修正しました。ご指摘ありがとうございます。

    init()メソッドについてですが、ご指摘通りですね。
    init()自体あっても害は無いし、コメントでご指摘を受けていますし、何より自分で一生懸命考えて書いたコードなのでこのままにしておくことにします。

    ◆以下超言い訳◆
    コンストラクタから仮想関数を呼ばないのが望ましいという点からクラスメソッドに出来るだけinit()を用意する(なにも処理がなくても)ようにしているのでつい用意してしまいました。

    返信削除