日韩成人免费在线_国产成人一二_精品国产免费人成电影在线观..._日本一区二区三区久久久久久久久不

當(dāng)前位置:首頁(yè) > 科技  > 軟件

C++之單例的幾種寫法

來(lái)源: 責(zé)編: 時(shí)間:2023-11-13 17:17:21 329觀看
導(dǎo)讀單例可以說(shuō)是眾多設(shè)計(jì)模式中最常用的了,同時(shí)單例設(shè)計(jì)模式也是一個(gè)老生常談的問(wèn)題,這是因?yàn)閷懸粋€(gè)單例卻是很簡(jiǎn)單,但是想要寫好一個(gè)單例卻比較難。首先我們先來(lái)理一下在C++中實(shí)現(xiàn)單例最基本的幾個(gè)步驟:私有化構(gòu)造函數(shù)、拷

單例可以說(shuō)是眾多設(shè)計(jì)模式中最常用的了,同時(shí)單例設(shè)計(jì)模式也是一個(gè)老生常談的問(wèn)題,這是因?yàn)閷懸粋€(gè)單例卻是很簡(jiǎn)單,但是想要寫好一個(gè)單例卻比較難。1X828資訊網(wǎng)——每日最新資訊28at.com

首先我們先來(lái)理一下在C++中實(shí)現(xiàn)單例最基本的幾個(gè)步驟:1X828資訊網(wǎng)——每日最新資訊28at.com

  • 私有化構(gòu)造函數(shù)、拷貝構(gòu)造函數(shù)、賦值運(yùn)算符等;
  • 確保線程安全;
  • static靜態(tài)變量只初始化一次;

1X828資訊網(wǎng)——每日最新資訊28at.com

單例的幾種模式

1.最簡(jiǎn)單的餓漢模式

#include <iostream>class Singleton {private:    // 聲明    static Singleton* instance;    int a{0};    Singleton() {        std::cout << "Singleton 構(gòu)造函數(shù)" << std::endl;    }    Singleton(const Singleton& temp) {        std::cout << "Singleton 拷貝構(gòu)造函數(shù)" << std::endl;    }    Singleton& operator = (const Singleton& temp){        return *instance;    }public:    static Singleton* getInstance() {        return instance;    }    void addA(){        a++;    }    void printA(){        std::cout << "printA:" << a << std::endl;    }};// 類靜態(tài)變量要在類內(nèi)聲明,類外定義Singleton *Singleton::instance = new Singleton;int main() {    Singleton* singleton = Singleton::getInstance();    singleton->addA();    singleton->printA();    return 0;}

這種寫法常用,但是也藏了一些隱患,比如如果使用者自作聰明在通過(guò)函數(shù)getInstance獲取到了單例指針,使用完畢后調(diào)用delete刪除了指針那怎么辦? 請(qǐng)問(wèn)作為"資深"的復(fù)制粘貼工程師你知道怎么避免這種情況嗎?1X828資訊網(wǎng)——每日最新資訊28at.com

一把情況下我們?nèi)绻幌M_(kāi)發(fā)者調(diào)用delete刪除指針,可以直接重載delete函數(shù),并且將其設(shè)置偉私有方法,或者在C++11以上我們直接使用delete關(guān)鍵字將delete函數(shù)禁用掉。1X828資訊網(wǎng)——每日最新資訊28at.com

上面的代碼例子是指針形式的單例,當(dāng)然你也可以試試非指針式的單例書寫,其實(shí)更推薦非指針式的單例。1X828資訊網(wǎng)——每日最新資訊28at.com

2.加鎖的餓漢模式

#include <iostream>#include <mutex>class Singleton {private:    int a{0};    // 聲明    static std::mutex mtx;    static Singleton* instance;    Singleton(){    }    Singleton(const Singleton& temp) {        std::cout << "Singleton 拷貝構(gòu)造函數(shù)" << std::endl;    }    Singleton& operator=(const Singleton& temp){        return *instance;    }public:    static Singleton* getInstance() {        // 鎖、雙重判斷        if(nullptr == instance){            mtx.lock();            if (nullptr == instance) {                instance = new Singleton();            }            mtx.unlock();        }        return instance;    }    void addA(){        a++;    }    void printA(){        std::cout << "printA:" << a << std::endl;    }};// 需要定義std::mutex Singleton::mtx;Singleton *Singleton::instance{nullptr};int main() {    Singleton* singleton = Singleton::getInstance();    singleton->addA();    singleton->printA();    return 0;}

想用懶加載模式,同時(shí)為了保證線程安全,以上代碼是很多童鞋會(huì)寫出的示例代碼,然而在C++上述代碼卻并不能一定保證正確。1X828資訊網(wǎng)——每日最新資訊28at.com

這是因?yàn)槌绦蛟趫?zhí)行的過(guò)程中,出于效率的考量,兩個(gè)(在當(dāng)前線程中)沒(méi)有依賴的指令可能會(huì)調(diào)換順序執(zhí)行也就是 CPU 動(dòng)態(tài)調(diào)度。對(duì)于 CPU 來(lái)說(shuō),這已經(jīng)是幾十年的老技術(shù)了, 這里就不多說(shuō)了。1X828資訊網(wǎng)——每日最新資訊28at.com

因此以上這個(gè)鎖加雙重判斷的懶漢模式既繁瑣又不安全,并不推薦。1X828資訊網(wǎng)——每日最新資訊28at.com

3.C++11之后新特性std::call_once的模式

在單例的實(shí)現(xiàn)中,我們實(shí)際上是希望實(shí)現(xiàn)「執(zhí)行且只執(zhí)行一次」的語(yǔ)義。這在 C++11 之后,標(biāo)準(zhǔn)庫(kù)實(shí)際已經(jīng)提供了這樣的實(shí)現(xiàn)。 那就是std::once_flag和std::call_once。它們內(nèi)部利用互斥量和條件變量組合,實(shí)現(xiàn)了「執(zhí)行且只執(zhí)行一次」這樣的語(yǔ)義。1X828資訊網(wǎng)——每日最新資訊28at.com

下面我們看看使用std::once_flag和std::call_once實(shí)現(xiàn)的單例代碼實(shí)例:1X828資訊網(wǎng)——每日最新資訊28at.com

#include <iostream>#include <mutex>class Singleton {private:    int a{0};    // 聲明    static std::once_flag flag;    static Singleton* instance;    Singleton(){        std::cout << "Singleton 構(gòu)造函數(shù)" << std::endl;    }    Singleton(const Singleton& temp) {        std::cout << "Singleton 拷貝構(gòu)造函數(shù)" << std::endl;    }    Singleton& operator=(const Singleton& temp){        return *instance;    }public:    static Singleton* getInstance() {        std::call_once(flag, [&]() -> void {            instance = new Singleton;        });        return instance;    }    void addA(){        a++;    }    void printA(){        std::cout << "printA:" << a << std::endl;    }};// 需要定義std::once_flag Singleton::flag;Singleton *Singleton::instance{nullptr};int main() {    Singleton* singleton = Singleton::getInstance();    singleton->addA();    singleton->printA();    Singleton::getInstance()->addA();    Singleton::getInstance()->printA();    return 0;}

實(shí)例代碼運(yùn)行結(jié)果:1X828資訊網(wǎng)——每日最新資訊28at.com

1X828資訊網(wǎng)——每日最新資訊28at.com

需要注意的是,所有的 std::once_flag 內(nèi)部共享了同一對(duì)互斥量和條件變量。因此當(dāng)存在很多 std::call_once 的時(shí)候,性能會(huì)有所下降。 但是從另外一個(gè)角度想想如果一個(gè)程序中存在很多的std::call_once,那么這個(gè)程序本身就設(shè)計(jì)得很不合理,這種情況更應(yīng)該從程序設(shè)計(jì)的源頭上避免。1X828資訊網(wǎng)——每日最新資訊28at.com

4.函數(shù)內(nèi)static變量的模式

在 C++11 之后,C++標(biāo)準(zhǔn)保證函數(shù)靜態(tài)成員的初始化是線程安全的,對(duì)其讀寫則不保證線程安全。既然如此,那么我在直接在函數(shù)內(nèi)部使用static 修飾一個(gè)單例變量不就好了么?1X828資訊網(wǎng)——每日最新資訊28at.com

精簡(jiǎn)一下代碼如下:1X828資訊網(wǎng)——每日最新資訊28at.com

#include <iostream>class Singleton {private:    int a{0};    Singleton(){        std::cout << "Singleton 構(gòu)造函數(shù)" << std::endl;    }    Singleton(const Singleton& temp) {        std::cout << "Singleton 拷貝構(gòu)造函數(shù)" << std::endl;    }    Singleton& operator=(const Singleton& temp){        return *this;    }public:    static Singleton* getInstance() {        static Singleton instance;        return &instance;    }    void addA(){        a++;    }    void printA(){        std::cout << "printA:" << a << std::endl;    }};int main() {    Singleton* singleton = Singleton::getInstance();    singleton->addA();    singleton->printA();    Singleton::getInstance()->addA();    Singleton::getInstance()->printA();    return 0;}

以上代碼實(shí)現(xiàn)的單例即是線程安全,同時(shí)也是懶加載的,這就是在C++11之后,Effective C++最推薦的單例模式寫法。1X828資訊網(wǎng)——每日最新資訊28at.com

模版形式的單例

實(shí)現(xiàn)一個(gè)類模板,其模板參數(shù)是希望由單例管理的類的名字,并提供 getInstance 之類的靜態(tài)接口。這種做法的好處是希望被單例管理的類,可以自由編寫,而無(wú)需繼承基類;并且在需要的時(shí)候,可以隨時(shí)脫去單例外衣。1X828資訊網(wǎng)——每日最新資訊28at.com

#include <iostream>template <typename T>struct Singleton {    static T* getInstance() {        static T ins;        return &ins;    }};class A{private:    int a{0};    A(const A& tmp){        std::cout << "A拷貝構(gòu)造函數(shù)" << std::endl;    }    A& operator=(const A& tmp){        std::cout << "A賦值運(yùn)算符" << std::endl;        return *this;    }public:    A(){        std::cout << "A構(gòu)造函數(shù)" << std::endl;    }    void addA(){        a++;    }    void printA(){        std::cout << "printA:" << a << std::endl;    }};int main() {    A* singleton = Singleton<A>::getInstance();    singleton->addA();    singleton->printA();    A* singleton1 = Singleton<A>::getInstance();    singleton1->addA();    singleton1->printA();    return 0;}

由上面的代碼可以看出,單例管理就交給了模版Singleton去控制了,類A本身就不知乎嚴(yán)格控制自己是否是單例了,這種實(shí)現(xiàn)就比較的靈活,如果你想使用單例的類A就搭配Singleton的模版進(jìn)行使用即可, 如果你想使用非單例的類A就像正常那樣使用即可。1X828資訊網(wǎng)——每日最新資訊28at.com

本文鏈接:http://www.www897cc.com/showinfo-26-23604-0.htmlC++之單例的幾種寫法

聲明:本網(wǎng)頁(yè)內(nèi)容旨在傳播知識(shí),若有侵權(quán)等問(wèn)題請(qǐng)及時(shí)與本網(wǎng)聯(lián)系,我們將在第一時(shí)間刪除處理。郵件:2376512515@qq.com

上一篇: C++異常處理:如何使用try、catch、throw

下一篇: 深入理解序列化:概念、應(yīng)用與技術(shù)

標(biāo)簽:
  • 熱門焦點(diǎn)
Top 主站蜘蛛池模板: 孝昌县| 开远市| 修武县| 石景山区| 阳泉市| 芦溪县| 蓝山县| 浮山县| 墨竹工卡县| 界首市| 贺兰县| 偃师市| 通渭县| 成武县| 靖州| 黑河市| 湖南省| 靖边县| 兴和县| 邹城市| 靖州| 天峻县| 金阳县| 咸阳市| 杂多县| 迁安市| 赣州市| 中牟县| 三河市| 泰州市| 利辛县| 疏附县| 滨州市| 育儿| 博湖县| 杭州市| 绥阳县| 襄汾县| 瑞金市| 鄯善县| 屏边|