--- tags: c++, singleton, tutorial, lintConfig: MD013: false --- # C++ Singleton ## What is singleton Singleton是整個類別(class)只會有一個實例(instance)存在,有利於系統控制整體的行為。 According to [wiki](https://en.wikipedia.org/wiki/Singleton_pattern): > The singleton design pattern solves problems by allowing it to: > > - Ensure that a class only has one instance > - Easily access the sole instance of a class > - Control its instantiation > - Restrict the number of instances > - Access a global variable > > The singleton design pattern describes how to solve such problems: > > - Hide the constructors of the class. > - Define a public static operation (getInstance()) that returns the sole instance of the class. > ## Eager singleton & lazy singleton ### Eager singleton Eager版本會在main前,class被定義時就初始化完成,因為static member variable會被使為全域變數。而缺點為會在main開始執行之前初始化此class,萬一class初始資源龐大時間久,則會影響到main開始的執行時間。 ```cpp= #include <iostream> using namespace std; class EagerSingleton { public: static EagerSingleton &getInstance() { return gInstance; } // copy contructor EagerSingleton(const EagerSingleton &) = delete; // assignment operator EagerSingleton &operator=(const EagerSingleton &) = delete; private: static EagerSingleton gInstance; EagerSingleton() { cout << "EagerSingleton contructor called!\n"; } ~EagerSingleton() { cout << "EagerSingleton destructor called!\n"; } }; /* class definition */ EagerSingleton EagerSingleton::gInstance; /* main function */ int main(void) { cout << "Main started!\n"; cout << "getInstance() addr: " << &EagerSingleton::getInstance() << '\n'; EagerSingleton& s = EagerSingleton::getInstance(); cout << "reference object addr: " << &s << '\n'; return 0; } ``` public中用`getInstane` 回傳`gInstance`的reference。將copy constructor和assignment operator失去作用以免不小心複製instance。`gInstance` 為一個private的static member。而contructor以及destructor放在private中即不可用宣告建立此物件。 在 main 中呼叫 `getInstance`. 最終output: ```bash= $ ./eager_singleton EagerSingleton contructor called! Main started! getInstance() addr: 0x5607a00a4191 reference object addr: 0x5607a00a4191 EagerSingleton destructor called! ``` 可以看到在 main 開始執行前,class 已被初始化。並且在main結束時 destructor 會被呼叫。 ### Lazy singleton Lazy singleton 在被用到時才會初始化, ```cpp= #include <iostream> using namespace std; class LazySingleton { public: static LazySingleton* getInstance() { cout << "Get LazySingleton instance\n"; if (gInstance == nullptr) { gInstance = new LazySingleton(); } return gInstance; } // copy contructor LazySingleton(const LazySingleton &) = delete; // assignment operator LazySingleton &operator=(const LazySingleton &) = delete; private: static LazySingleton *gInstance; LazySingleton() { cout << "LazySingleton contructor called!\n"; } ~LazySingleton() { cout << "LazySingleton destructor called!\n"; } }; /* class definition */ LazySingleton *LazySingleton::gInstance = nullptr; /* main function */ int main(void) { cout << "Main started!\n"; cout << "getInstance() addr: " << LazySingleton::getInstance() << '\n'; LazySingleton* s = LazySingleton::getInstance(); cout << "reference object addr: " << s << '\n'; return 0; } ``` ```bash= $ ./lazy_singleton Main started! getInstance() addr: LazySingleton contructor called! 0x5629aced42c0 reference object addr: 0x5629aced42c0 ``` ```cpp= #include <iostream> using namespace std; class LazySingleton { public: static LazySingleton& getInstance() { cout << "Get LazySingleton instance\n"; if (gInstance == nullptr) { gInstance = new LazySingleton(); } return *gInstance; } // copy contructor LazySingleton(const LazySingleton &) = delete; // assignment operator LazySingleton &operator=(const LazySingleton &) = delete; private: static LazySingleton *gInstance; LazySingleton() { cout << "LazySingleton contructor called!\n"; } ~LazySingleton() { cout << "LazySingleton destructor called!\n"; } }; /* class definition */ LazySingleton *LazySingleton::gInstance = nullptr; /* main function */ int main(void) { cout << "Main started!\n"; cout << "getInstance() addr: " << &LazySingleton::getInstance() << '\n'; LazySingleton& s = LazySingleton::getInstance(); cout << "reference object addr: " << &s << '\n'; return 0; } ```