c++的單例模式及c++11對單例模式的最佳化

來源:互聯網
上載者:User

標籤:fprintf   加鎖   實現   導致   bsp   str   second   面試官   優點   

單例模式

單例模式,可以說設計模式中最常應用的一種模式了,據說也是面試官最喜歡的題目。但是如果沒有學過設計模式的人,可能不會想到要去應用單例模式,面對單例模式適用的情況,可能會優先考慮使用全域或者靜態變數的方式,這樣比較簡單,也是沒學過設計模式的人所能想到的最簡單的方式了。

一般情況下,我們建立的一些類是屬於工具性質的,基本不用儲存太多的跟自身有關的資料,在這種情況下,每次都去new一個對象,即增加了開銷,也使得代碼更加臃腫。其實,我們只需要一個執行個體對象就可以。如果採用全域或者靜態變數的方式,會影響封裝性,難以保證別的代碼不會對全域變數造成影響。

考慮到這些需要,我們將預設的建構函式聲明為私人的,這樣就不會被外部所new了,甚至可以將解構函式也聲明為私人的,這樣就只有自己能夠刪除自己了。在Java和C#這樣純的物件導向的語言中,單例模式非常好實現,直接就可以在靜態區初始化instance,然後通過getInstance返回,這種就被稱為餓漢式單例類。也有些寫法是在getInstance中new instance然後返回,這種就被稱為懶漢式單例類,但這涉及到第一次getInstance的一個判斷問題。

下面的代碼只是表示一下,跟具體哪種語言沒有關係。

單線程中:

Singleton* getInstance(){    if (instance == NULL)        instance = new Singleton();     return instance;}

這樣就可以了,保證只取得了一個執行個體。但是在多線程的環境下卻不行了,因為很可能兩個線程同時運行到if (instance == NULL)這一句,導致可能會產生兩個執行個體。於是就要在代碼中加鎖。

Singleton* getInstance(){    lock();    if (instance == NULL)    {       instance = new Singleton();    }    unlock();    return instance;}

但這樣寫的話,會稍稍映像效能,因為每次判斷是否為空白都需要被鎖定,如果有很多線程的話,就愛會造成大量線程的阻塞。於是出現了雙重鎖定。

Singleton* getInstance(){    if (instance == NULL)    {    lock();        if (instance == NULL)        {               instance = new Singleton();        }        unlock();    }    return instance;}

這樣只夠極低的幾率下,通過越過了if (instance == NULL)的線程才會有進入鎖定臨界區的可能性,這種幾率還是比較低的,不會阻塞太多的線程,但為了防止一個線程進入臨界區建立執行個體,另外的線程也進去臨界區建立執行個體,又加上了一道防禦if (instance == NULL),這樣就確保不會重複建立了。

常用的情境

單例模式常常與原廠模式結合使用,因為工廠只需要建立產品執行個體就可以了,在多線程的環境下也不會造成任何的衝突,因此只需要一個工廠執行個體就可以了。

 

優點

1.減少了時間和空間的開銷(new執行個體的開銷)。

2.提高了封裝性,使得外部不易改動執行個體。

 

缺點

1.懶漢式是以時間換空間的方式。(上面使用的方式)

2.餓漢式是以空間換時間的方式。(下面使用的方式)

 

#ifndef _SINGLETON_H_#define _SINGLETON_H_class Singleton{public:    static Singleton* getInstance();private:    Singleton();    //把複製建構函式和=操作符也設為私人,防止被複製    Singleton(const Singleton&);    Singleton& operator=(const Singleton&);    static Singleton* instance;};#endif #include "Singleton.h"Singleton::Singleton(){}Singleton::Singleton(const Singleton&){}Singleton& Singleton::operator=(const Singleton&){}//在此處初始化Singleton* Singleton::instance = new Singleton();Singleton* Singleton::getInstance(){    return instance;} #include "Singleton.h"#include <stdio.h>int main(){    Singleton* singleton1 = Singleton::getInstance();    Singleton* singleton2 = Singleton::getInstance();    if (singleton1 == singleton2)        fprintf(stderr,"singleton1 = singleton2\n");    return 0;}

 

以上使用的方式存在問題:只能執行個體化沒有參數的類型,其它帶參數的類型就不行了。

c++11 為我們提供瞭解決方案:可變模板參數

  

template <typename T>class Singleton{public:template<typename... Args>  static T* Instance(Args&&... args)  {        if(m_pInstance==nullptr)            m_pInstance = new T(std::forward<Args>(args)...);        return m_pInstance;    }  static T* GetInstance()      {            if (m_pInstance == nullptr)                  throw std::logic_error("the instance is not init, please initialize the instance first");            return m_pInstance;      }static void DestroyInstance()    {        delete m_pInstance;        m_pInstance = nullptr;    }private:        Singleton(void);        virtual ~Singleton(void);        Singleton(const Singleton&);        Singleton& operator = (const Singleton&);private:    static T* m_pInstance;};template <class T> T*  Singleton<T>::m_pInstance = nullptr;

由於原來的介面中,單例對象的初始化和取值都是一個介面,可能會遭到誤用,更新之後,講初始化和取值分為兩個介面,單例的用法為:先初始化,後面取值,如果中途銷毀單例的話,需要重新取值。如果沒有初始化就取值則會拋出一個異常。

 

Multiton的實現

#include <map>#include <string>#include <memory>using namespace std;template < typename T, typename K = string>class Multiton{public:    template<typename... Args>    static std::shared_ptr<T> Instance(const K& key, Args&&... args)    {        return GetInstance(key, std::forward<Args>(args)...);    }    template<typename... Args>    static std::shared_ptr<T> Instance(K&& key, Args&&... args)    {        return GetInstance(key, std::forward<Args>(args)...);    }private:    template<typename Key, typename... Args>    static std::shared_ptr<T> GetInstance(Key&& key, Args&&...args)    {        std::shared_ptr<T> instance = nullptr;        auto it = m_map.find(key);        if (it == m_map.end())        {            instance = std::make_shared<T>(std::forward<Args>(args)...);            m_map.emplace(key, instance);        }        else        {            instance = it->second;        }        return instance;    }private:    Multiton(void);    virtual ~Multiton(void);    Multiton(const Multiton&);    Multiton& operator = (const Multiton&);private:    static map<K, std::shared_ptr<T>> m_map;};template <typename T, typename K>map<K, std::shared_ptr<T>> Multiton<T, K>::m_map;

 

 

c++的單例模式及c++11對單例模式的最佳化

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.