C 中實現Singleton的正確方法

2021-06-03 00:55:58 字數 3993 閱讀 3502

如果某個類管理了系統中唯一的某種資源,那麼我們只能建立該類的乙個例項,此時用到singleton設計模式(後面為了簡化將省略「設計模式」四個字)就比較合適了。然而,如果不注意實現方法,就很有可能會讓我們碰到一些莫名其妙的錯誤。圖1是經過簡化所得到的乙個實現錯誤的例子。

main.c00001: #include 00002:

00003: using namespace std;

00004:

00005: class singleton1_t

00006:

00012:

00013: void count_increase ()

00014: int count () const

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:

00031:

00032: private:

00033: singleton2_t ()

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:

圖1

圖中的兩個類在實現singleton時都將類的構造和析構函式的對外可視性設為private,這是實現singleton首先要注意的乙個點。通過這一手段,有助於預防他人粗心地定義類例項。

圖中的singleton2_t類在其建構函式中呼叫singleton1_t類的count_increase ()方法使計數加一。第44行的**用於代表使用singleton2_t例項。第46行**則顯示singleton1_t類的記數資訊。圖 2示例了該程式的執行結果。

$g++ main.cpp -o singleton.exe$./singleton.execount = 0

圖2

是不是對於最終的顯示計數為0而不是1感到奇怪?錯誤發生的原因在於,singleton2_t類例項的構造是先於singleton1_t類的,當singleton1_t類的例項在最後構造時會把count_變數置成0,從而覆蓋singleton2_t的建構函式所引起的變更。

儘管這是乙個精心設計的錯誤,但在大型專案中出現這類錯誤的可能性卻並不小。因為在現實專案中,singleton1_t和singleton2_t兩個類的實現很可能是在不同的原始檔中,這勢必造成兩個類例項的初始化順序會因鏈結順序不同而不同,《揭示c++中全域性類變數的構造與析構順序》一文介紹了這是為什麼。

在本例中,如果將第39行和第40行的**進行對調就不會出現這種奇怪的現象,但這不是解決問題的終極方法。更好的方法需要更改singleton的實現方法,圖 3示例了一種新的實現方法。

main.c00001: #include 00002:

00003: using namespace std;

00004:

00005: class singleton1_t

00006:

00013: return p_instance_;

00014: }

00015:

00016: void count_increase ()

00017: int count () const

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:

00035: return p_instance_;

00036: }

00037:

00038: private:

00039: singleton2_t ()

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:

圖3

新實現最大的變化,在於將以前的類靜態變數從類例項變成了類指標,並在instance()函式中需要時通過new操作符建立類例項。指標在c++中仍是當作一種原始資料型別處理的,其初始化與類例項的初始化不同,不需呼叫類建構函式。在這一實現中,兩個類的靜態變數p_instance_的初始化都是在程式的.bss段初始化時一次性完成的。

這一實現中由於類的例項是通過new操作符獲得的,所以需要為類定義釋放例項的函式(圖中省略了),並由在合適的時機呼叫。為了省去這類麻煩,作者更推崇圖 4所示的實現方式。

main.c00005: class singleton1_t

00006:

00014: return p_instance_;

00015: }

00016:

00017: void count_increase ()

00018: int count () const

00019:

00020: private:

00021: singleton1_t (): count_ (0) {}

00022: ~singleton1_t () {}

00023:

00024: static singleton1_t *p_instance_;

00025: int count_;

00026: };

圖4

通過在函式內部定義靜態變數的方法獲得類例項,一方面簡化了類介面的實現,另一方面又降低了因為忘記呼叫釋放介面函式而導致記憶體洩漏的可能。需要提醒的是,在這種實現方法中,類例項的構造是發生在各類的instance()函式第一次被呼叫時,而各例項的析構又是以與構造相反的順序進行的,且後者是由程式語言環境所保證的。

本文出自 「李雲」 部落格,請務必保留此出處 

C 中的 Singleton 實現

ansi c 中的 singleton 實現說難不難,說容易也不容易,很多人寫 ansi c 的 singleton class 都有錯誤。這篇文章討論怎樣在 ansi c 中寫 singleton class,希望對大家有幫助。設計模式 中把 singleton 寫成返回指標 class sing...

C 中的 Singleton 實現

c 中的 singleton 實現 關鍵字 ansi c singleton,static member,initialize,auto ptr,std,stl implement,實現 ansi c 中的 singleton 實現說難不難,說容易也不容易,很多人寫 ansi c 的 singlet...

C 中的Singleton 類的實現

ansi c 中的 singleton 實現說難不難,說容易也不容易,很多人寫 ansi c 的 singleton class 都有錯誤。這篇文章討論怎樣在 ansi c 中寫 singleton class,希望對大家有幫助。設計模式 中把 singleton 寫成返回指標 class sing...