單例模式,也叫單子模式,是一種常用的軟體設計模式。在應用這個模式時,單例對象的類必須保證隻有一個執行個體存在。許多時候整個系統隻需要擁有一個的全局對象,這樣有利于我們協調系統整體的行為。比如在某個伺服器程式中,該伺服器的配置資訊存放在一個檔案中,這些配置資料由一個單例對象統一讀取,然後服務程序中的其他對象再通過這個單例對象擷取這些配置資訊。這種方式簡化了在複雜環境下的配置管理。
實作單例模式的思路是:一個類能傳回對象一個引用(永遠是同一個)和一個獲得該執行個體的方法(必須是靜态方法,通常使用getInstance這個名稱);當我們調用這個方法時,如果類持有的引用不為空就傳回這個引用,如果類保持的引用為空就建立該類的執行個體并将執行個體的引用賦予該類保持的引用;同時我們還将該類的構造函數定義為私有方法,這樣其他處的代碼就無法通過調用該類的構造函數來執行個體化該類的對象,隻有通過該類提供的靜态方法來得到該類的唯一執行個體。
既然單例模式隻允許存在一個對象,那麼對象的拷貝,指派就都是不允許的,是以把拷貝構造函數、指派操作符全部聲明為private
第一種實作
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<code>class</code> <code>Singleton</code>
<code>{</code>
<code> </code><code>private</code><code>:</code>
<code> </code><code>static</code> <code>Singleton *m_instance;</code>
<code> </code><code>Singleton(){}</code><code>//隐藏構造函數</code>
<code> </code><code>Singleton(</code><code>const</code> <code>Singleton &){}</code><code>//隐藏拷貝構造函數</code>
<code> </code><code>Singleton& operator=(</code><code>const</code> <code>Singleton &a){}</code><code>//隐藏指派操作符</code>
<code> </code><code>~Singleton(){}</code><code>//隐藏析構函數</code>
<code> </code><code>public</code><code>:</code>
<code> </code><code>static</code> <code>Singleton *getInstance()</code>
<code> </code><code>{</code>
<code> </code><code>if</code><code>(m_instance == NULL)</code>
<code> </code><code>m_instance =</code><code>new</code> <code>Singleton;</code>
<code> </code><code>return</code> <code>m_instance;</code>
<code> </code><code>}</code>
<code> </code>
<code>};</code>
<code>Singleton * Singleton::m_instance = NULL;</code>
這種實作很明顯滿足單例模式的要求,但是有兩個問題 (1)我們new的對象沒有被delete,(2)這種設計不是線程安全的(兩個線程可能同時判斷m_instance == NULL,這樣就有兩個執行個體被建立了),為了解決上面的問題,第二種實作如下
第二種實作
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
<code> </code><code>class</code> <code>DelInstance</code>
<code> </code><code>{</code>
<code> </code><code>public</code><code>:</code>
<code> </code><code>~DelInstance()</code>
<code> </code><code>{</code>
<code> </code><code>if</code><code>(Singleton::m_instance)</code>
<code> </code><code>delete</code> <code>Singleton::m_instance;</code>
<code> </code><code>}</code>
<code> </code><code>};</code>
<code> </code><code>static</code> <code>DelInstance delIns;</code><code>//負責回收new出來的對象;</code>
<code> </code><code>lock();</code><code>//加鎖(lock 和 unlock是随便寫的函數,c++本身沒有)</code>
<code> </code><code>if</code><code>(m_instance == NULL)</code>
<code> </code><code>m_instance =</code><code>new</code> <code>Singleton;</code>
<code> </code><code>unlock();</code><code>//釋放鎖</code>
<code>Singleton::DelInstance Singleton::delIns;</code>
這裡我們使用了另一個私有内嵌類DelInstance,在Singleton内定義了一個靜态的的對象delIns來負責回收new出來的對象,因為靜态對象delIns在程式結束時會自動調用自己析構函數進而釋放m_instance指向的記憶體,這裡新手可能會犯兩個錯誤:
1、不使用額外的類,直接把delete語句寫在singleton的析構函數中。這種做法是錯誤的,因為我們是通過new出來的singleton執行個體,程式結束時不會自動調用析構函數,再者如果真的調用了就會進入一個無限循環的狀态,即singleton的析構函數是為了删除一個singleton對象,删除該對象時,又會調用singleton的析構函數,這樣一直循環下去。
2、delIns成員不定義成static。這也是錯誤的,如果delIns不是static,那麼delIns就要等到singleton對象析構時才會析構,但是delIns的目的就是要析構singleton,這就沖突了。如果delIns是static的,他的生命期和他所在的類對象沒有關系,他相當于是全局的,當他的生命期到的時候就會自動析構,進而析構singleton。
getInstance中我們使用了double-check來保證線程安全,隻有當m_instance是NULL時,線程才會加鎖。這樣也保證了隻建立了一個對象執行個體。
第三種實作
<code>Singleton * Singleton::m_instance =</code><code>new</code> <code>Singleton;</code>
這是屬于餓漢模式,即一開始就建立一個類的執行個體,以後都傳回其位址,是線程安全的。
第四種實作
<code> </code><code>static</code> <code>Singleton s;</code>
<code> </code><code>return</code> <code>&s;</code>
<code>Singleton Singleton::s;</code>
餓漢模式,這種實作定義一個私有的靜态對象執行個體,(注意不能在類中定義自身的非靜态對象,因為這樣會形成無限循環定義,而靜态對象因為保證隻有一個副本,是以不會循環定義),這也是線程安全的
第五種實作
<code> </code><code>lock();</code><code>//c++11 可以不用加鎖</code>
<code> </code><code>static</code> <code>Singleton s;</code>
<code> </code><code>unlock();</code>
注意
本文轉自tenos部落格園部落格,原文連結:http://www.cnblogs.com/TenosDoIt/p/3639395.html,如需轉載請自行聯系原作者