如果某個類管理了系統(tǒng)中唯一的某種資源,,那么我們只能創(chuàng)建該類的一個實例,,此時用到singleton設(shè)計模式(后面為了簡化將省略“設(shè)計模式”四個字)就比較合適了。然而,,如果不注意實現(xiàn)方法,,就很有可能會讓我們碰到一些莫名其妙的錯誤。圖1是經(jīng)過簡化所得到的一個實現(xiàn)錯誤的例子,。 - main.c
- 00001: #include <iostream>
- 00002:
- 00003: using namespace std;
- 00004:
- 00005: class singleton1_t
- 00006: {
- 00007: public:
- 00008: static singleton1_t *instance ()
- 00009: {
- 00010: return &instance_;
- 00011: }
- 00012:
- 00013: void count_increase () {count_ ++;}
- 00014: int count () const {return count_;}
- 00015:
- 00016: private:
- 00017: singleton1_t (): count_ (0) {}
- 00018: ~singleton1_t () {}
- 00019:
- 00020: static singleton1_t instance_;
- 00021: int count_;
- 00022: };
- 00023:
- 00024: class singleton2_t
- 00025: {
- 00026: public:
- 00027: static singleton2_t *instance ()
- 00028: {
- 00029: return &instance_;
- 00030: }
- 00031:
- 00032: private:
- 00033: singleton2_t () {singleton1_t::instance ()->count_increase ();}
- 00034: ~singleton2_t () {}
- 00035:
- 00036: static singleton2_t instance_;
- 00037: };
- 00038:
- 00039: singleton2_t singleton2_t::instance_;
- 00040: singleton1_t singleton1_t::instance_;
- 00041:
- 00042: int main ()
- 00043: {
- 00044: (void) singleton2_t::instance ();
- 00045: cout << "count = " << singleton1_t::instance ()->count () << endl;
- 00046: return 0;
- 00047: }
圖1 圖中的兩個類在實現(xiàn)singleton時都將類的構(gòu)造和析構(gòu)函數(shù)的對外可視性設(shè)為private,,這是實現(xiàn)singleton首先要注意的一個點。通過這一手段,,有助于預(yù)防他人粗心地定義類實例,。 圖中的singleton2_t類在其構(gòu)造函數(shù)中調(diào)用singleton1_t類的count_increase ()方法使計數(shù)加一。第44行的代碼用于代表使用singleton2_t實例,。第46行代碼則顯示singleton1_t類的記數(shù)信息,。圖2示例了該程序的運行結(jié)果。
- $ g++ main.cpp -o singleton.exe
- $ ./singleton.exe
- count = 0
圖2
是不是對于最終的顯示計數(shù)為0而不是1感到奇怪,?錯誤發(fā)生的原因在于,,singleton2_t類實例的構(gòu)造是先于singleton1_t類的,當singleton1_t類的實例在最后構(gòu)造時會把count_變量置成0,,從而覆蓋singleton2_t的構(gòu)造函數(shù)所引起的變更,。 盡管這是一個精心設(shè)計的錯誤,但在大型項目中出現(xiàn)這類錯誤的可能性卻并不小,。因為在現(xiàn)實項目中,,singleton1_t和singleton2_t兩個類的實現(xiàn)很可能是在不同的源文件中,這勢必造成兩個類實例的初始化順序會因鏈接順序不同而不同,,《揭示C++中全局類變量的構(gòu)造與析構(gòu)順序》一文介紹了這是為什么,。 在本例中,如果將第39行和第40行的代碼進行對調(diào)就不會出現(xiàn)這種奇怪的現(xiàn)象,,但這不是解決問題的終極方法,。更好的方法需要更改singleton的實現(xiàn)方法,圖3示例了一種新的實現(xiàn)方法,。
- main.c
- 00001: #include <iostream>
- 00002:
- 00003: using namespace std;
- 00004:
- 00005: class singleton1_t
- 00006: {
- 00007: public:
- 00008: static singleton1_t *instance ()
- 00009: {
- 00010: if (0 == p_instance_) {
- 00011: p_instance_ = new singleton1_t;
- 00012: }
- 00013: return p_instance_;
- 00014: }
- 00015:
- 00016: void count_increase () {count_ ++;}
- 00017: int count () const {return count_;}
- 00018:
- 00019: private:
- 00020: singleton1_t (): count_ (0) {}
- 00021: ~singleton1_t () {}
- 00022:
- 00023: static singleton1_t *p_instance_;
- 00024: int count_;
- 00025: };
- 00026:
- 00027: class singleton2_t
- 00028: {
- 00029: public:
- 00030: static singleton2_t *instance ()
- 00031: {
- 00032: if (0 == p_instance_) {
- 00033: p_instance_ = new singleton2_t;
- 00034: }
- 00035: return p_instance_;
- 00036: }
- 00037:
- 00038: private:
- 00039: singleton2_t () {singleton1_t::instance ()->count_increase ();}
- 00040: ~singleton2_t () {}
- 00041:
- 00042: static singleton2_t *p_instance_;
- 00043: };
- 00044:
- 00045: singleton2_t *singleton2_t::p_instance_ = 0;
- 00046: singleton1_t *singleton1_t::p_instance_ = 0;
- 00047:
- 00048: int main ()
- 00049: {
- 00050: singleton2_t::instance ();
- 00051: cout << "count = " << singleton1_t::instance ()->count () << endl;
- 00052: return 0;
- 00053: }
圖3
新實現(xiàn)最大的變化,,在于將以前的類靜態(tài)變量從類實例變成了類指針,并在instance()函數(shù)中需要時通過new操作符創(chuàng)建類實例,。指針在C++中仍是當作一種原始數(shù)據(jù)類型處理的,,其初始化與類實例的初始化不同,,不需調(diào)用類構(gòu)造函數(shù)。在這一實現(xiàn)中,,兩個類的靜態(tài)變量p_instance_的初始化都是在程序的.bss段初始化時一次性完成的,。 這一實現(xiàn)中由于類的實例是通過new操作符獲得的,所以需要為類定義釋放實例的函數(shù)(圖中省略了),,并由在合適的時機調(diào)用,。為了省去這類麻煩,作者更推崇圖4所示的實現(xiàn)方式,。 - main.c
- 00005: class singleton1_t
- 00006: {
- 00007: public:
- 00008: static singleton1_t *instance ()
- 00009: {
- 00010: if (0 == p_instance_) {
- 00011: static singleton1_t instance;
- 00012: p_instance_ = &instance;
- 00013: }
- 00014: return p_instance_;
- 00015: }
- 00016:
- 00017: void count_increase () {count_ ++;}
- 00018: int count () const {return count_;}
- 00019:
- 00020: private:
- 00021: singleton1_t (): count_ (0) {}
- 00022: ~singleton1_t () {}
- 00023:
- 00024: static singleton1_t *p_instance_;
- 00025: int count_;
- 00026: };
圖4 通過在函數(shù)內(nèi)部定義靜態(tài)變量的方法獲得類實例,,一方面簡化了類接口的實現(xiàn),另一方面又降低了因為忘記調(diào)用釋放接口函數(shù)而導(dǎo)致內(nèi)存泄漏的可能,。需要提醒的是,,在這種實現(xiàn)方法中,類實例的構(gòu)造是發(fā)生在各類的instance()函數(shù)第一次被調(diào)用時,,而各實例的析構(gòu)又是以與構(gòu)造相反的順序進行的,,且后者是由編程語言環(huán)境所保證的。
|