最簡單的單例實現只需要乙個全域性物件:
some_classthe_instance;
some_class *get_instance
()
這個實現簡單到沒什麼可說,它在絕大多數情況下能正常工作,之所以說是「絕大多數」,主要原因是:2、構造的時間點其實是不確定的,c++標準只要求這個物件在第一次被使用前,它的建構函式會被呼叫,具體實現中的保證甚至還達不到這個強度,絕大部分的實現只能保證
main
之後的函式中使用這個物件時,建構函式會被呼叫。
3、這個實現可能會導致你例項化了根本沒用到的單例,因為「用到」這個物件的行為不一定能被c++準確識別,很多實現只是簡單的在
main
之前按定義的順序呼叫所有全域性物件的建構函式。
所以,既然你在用c++11,那我們就用c++11的新特性來實現這個功能。
這段程式在多執行緒環境裡也能正常工作,也能處理單例間的依賴,而且不用在意各種os/編譯器規定的各種坑爹初始化次序,任何編譯器,只要它正確實現了c++11標準中的thread部分,就能保證這一點。在c++11中引入了乙個新的東西叫
call_once
,配合once_flag
,你可以保證乙個函式只會被呼叫一次,有了這個東西,建立乙個單例的程式可以簡化成這樣:some_class *the_instance;
std::once_flag instance_created;
some_class *get_instance()
);return the_instance;
}
單例的銷毀其實也是個挺煩問題,最主要的問題是你很難準確的知道你從什麼時候開始才真得不再用它,乙個簡單的思路是atexit
放乙個析構函式在main
之後,但是正如你所預料,簡單的實現往往有問題。posix的atexit不是執行緒安全的,你需要用std::atexit
才能保證執行緒安全。
但即使是std::atexit
也不能正確處理單件之間的依賴,所以我們需要點更精細的辦法,比如引用計數。
引用計數最大的好處是物件的生存期真正的對應於物件的「有效期」,使用引用計數的版本變成了這樣:
std:
:shared_ptr the_instance;
std:
:once_flag instance_created;
std:
:shared_ptr get_instance()
);return the_instance;
}
全域性變數std::shared_ptrthe_instance
保證一旦物件建立之後,直到程式結束,它至少有乙個活引用,所以不會提前銷毀;另外任何依賴於它的物件都會導致引用計數增加,所以可以正確的處理依賴性。
當然,這個實現的代價是api介面變成了std::shared_ptr
,因為無論是裸指標還是引用,都不能正確的實現引用計數的語義,如果你對此還是不滿意,可以用boehm gc實現乙個相容裸指標和引用的版本。
ps. loadlibrary/dlopen最大的問題就是它們可能會跳過全域性初始化的部分,也就是說,所有「邏輯上」應該在main
之前執行的部分有可能根本就不會執行,由此會帶來各種各樣古怪的問題很難在這兒一一詳述,總之,避免全域性物件+loadlibrary/dlopen這種組合會極大的改善你的生活品質。
C 中實現單例模式
單例模式是軟體工程中廣為人知的設計模式。單例模式就是指乙個永遠只能例項化一次。使用的方式是呼叫類裡建立的靜態方法。通常來說,單例模式建立的類,都是不帶形參的 原因就是當建立多個例項的時候,如果引數不同的話 比如2個不同的過載建構函式 那麼就會造成一些不必要的問題 如果相同的例項要被建立而且他們使用相...
C 中的單例模式
c 中的單例模式 靜態成員經典應用 單例模式 在程式執行過程中,可能會希望某些類的例項物件永遠只有乙個 條件1.把建構函式私有化 2.定義乙個私有的靜態成員變數指標,用於指向單例物件 3.提供乙個公共的返回單例物件的靜態成員函式.class rocket return ms rocket stati...
C 中的單例模式
單例模式 物件只能出現一次 1.將建構函式私有化 2.使用乙個公開靜態方法 3.使用乙個 靜態屬性進行判斷當前視窗是否被建立。private regsiterwindow public static regsiterwindow selfwindow null public static regsi...