單例模式

一. static 關鍵字

1. 類中靜態變量和靜態成員函數
  • 1):類的靜態成員:
  • ①:靜態成員變量屬於整個類所有
  • ②:靜態成員變量的生命期不依賴於任何對象,爲程序的生命週期
  • ③:可以通過類名直接訪問公有靜態成員變量
  • ④:所有對象共享類的靜態成員變量
  • ⑥:可以通過對象名訪問公有靜態成員變量
  • ⑦:靜態成員變量需要在類外單獨分配空間
  • ⑧:靜態成員變量在程序內部位於全局數據區
  • 2):類的靜態成員函數:
  • 類的靜態成員函數有如下特性:
  • ①:靜態成員函數是類的一個特殊的成員函數
  • ②:靜態成員函數屬於整個類所有,沒有this指針
  • ③:靜態成員函數只能直接訪問靜態成員變量和靜態成員函數,因爲沒有this指針
  • ④:可以通過類名直接訪問類的公有靜態成員函數
  • ⑥:可以通過對象名訪問類的公有靜態成員函數
  • ⑦:定義靜態成員函數,直接使用static關鍵字修飾即可

#include <iostream>
using namespace std;

class testStatic {
public:
	//定義一個公有的靜態成員
	static int pub_value;
    testStatic() {};
    ~testStatic() {};
    //定義類的靜態成員函數
    static void pub_Print() {
        cout << pub_value << endl;
    }
    static void pri_Print() {
        cout << pri_value << endl;
    }
//定義類的靜態成員變量
private:
    static int pri_value;
};

//類的靜態成員需要在類外分配內存空間以及初始化。
//如果不初始化,則爲0
int testStatic::pri_value = 100;
int testStatic::pub_value = 100;

int main() {
	auto ptr = new testStatic();
    cout << ptr->pub_value << endl;
    ptr->pri_Print();
    ptr->pub_Print();
    testStatic::pri_Print();
    testStatic::pub_Print();
    return 0;
}

輸出:
100
100
100
100
100

二. 單例模式

敖 丙

  • 意圖:保證一個類僅有一個實例,並提供一個訪問它的全局訪問點。
  • 主要解決:一個全局使用的類頻繁地創建與銷燬。
  • 何時使用:當您想控制實例數目,節省系統資源的時候。
  • 如何解決:判斷系統是否已經有這個單例,如果有則返回,如果沒有則創建。
  • 關鍵代碼:構造函數是私有的。靜態成員函數返回該實例。
  • 實例:一些設備管理器常常設計爲單例模式,比如一個電腦有兩臺打印機,在輸出的時候就要處理不能兩臺打印機打印同一個文件。windows 系統中只能有一個窗口管理器,某個程序中只能有一個日誌輸出系統等。
  • 優點:
  • ①:在內存裏只有一個實例,減少了內存的開銷,尤其是頻繁的創建和銷燬實例(比如管理學院首頁頁面緩存)。
  • ②:避免對資源的多重佔用(比如寫文件操作)。
  • 缺點:
  • ①:沒有接口,不能繼承,與單一職責原則衝突,一個類應該只關心內部邏輯,而不關心外面怎麼樣來實例化。
  • 使用場景:
  • ①:要求生產唯一序列號。
  • ②:WEB 中的計數器,不用每次刷新都在數據庫里加一次,用單例先緩存起來。
  • ③:創建的一個對象需要消耗的資源過多,比如 I/O 與數據庫的連接等。
  • 注意事項:getInstance()方法中需要使用同步鎖 synchronized (Singleton.class)防止多線程同時進入造成 instance 被多次實例化。
  • 創建線程安全的單例有那些實現方法?

1. 餓漢模式:
  • 特點:一開始就初始化單例對象,不管需要不需要
  • 優點以及缺點:
  • 不用擔心多線程問題;
  • 可能程序中不需要這個單例對象,如果對象大的話,造成不嫩那個忍受的浪費。
#include <iostream>
#include <thread>
#include <vector>
using namespace std;

//餓漢模式
class hunSingleton{
public:
    //靜態公有成員函數,獲取單例對象
    static hunSingleton* getInstance() {
        return &hSingleton;
    }
    void print() {
        cout << "Print is called" << endl;
    }
private:
    //構造函數爲私有函數
    hunSingleton(){
        cout << "hunSingleton is called" << endl;
        hunSingleton::getInstance();
    };
    //私有static單例對象
    static hunSingleton hSingleton;
};

hunSingleton hunSingleton::hSingleton;

void fun1() {
    auto p = hunSingleton::getInstance();
    p->print();
}

int main() {
    auto ptr1 = hunSingleton::getInstance();
    auto ptr2 = hunSingleton::getInstance();
    cout << (ptr1==ptr2?"true":"false") << endl;
    vector<thread> threadPool;
    for(int i = 0; i < 10; i++) {
        threadPool.push_back(thread(fun1));
    }
    for(auto &ptr: threadPool) {
        ptr.join();
    }
    return 0;
}
//輸出:
hunSingleton is called
true
Print is called
Print is called
Print is called
Print is called
Print is called
Print is called
Print is called
Print is called
Print is called
Print is called
2. 懶漢模式:
  • 特點:不創建單例對象直到需要這個單例對象。
  • 優點以及缺點:
  • 不會造成資源浪費,如果不需要創建單例對象;
  • 但是多線程編程較爲複雜。
#include <vector>
#include <thread>
#include <iostream>
#include <mutex>
using namespace std;

//懶漢模式
class lazySingleton{
public:
    void print() {
        cout << "print is called" << endl;
    }
    static lazySingleton* getInstance() {
        if(lazyST_ == nullptr) {
            std::lock_guard<std::mutex> lock(mu_);
            if(lazyST_ == nullptr) {
                lazyST_ = new lazySingleton();
            }
        }
        return lazyST_;
    }
private:
    static std::mutex mu_;
    static lazySingleton* lazyST_;
    lazySingleton() {
        cout << "lazySingleton is called" << endl;
    };
};

std::mutex lazySingleton::mu_;
lazySingleton* lazySingleton::lazyST_ = nullptr;

void func2() {
    auto* ptr = lazySingleton::getInstance();
    ptr->print();
}

int main() {
    vector<thread> threadPool;
    for(int i = 0; i < 10; i++) {
        threadPool.push_back(thread(func2));
    }
    for(auto &p: threadPool) {
        p.join();
    }
    return 0;
}
//輸出
lazySingleton is called
print is called
print is called
print is called
print is called
print is called
print is called
print is called
print is called
print is called
print is called
3. C++11 簡化版
  • 可以利用 C++11 對 static 的改進性質簡化代碼。
  • static變量本身全局就只有一份,與單例對象的性質極其相似。而C++11爲了確保只初始化static變量一次,提供了兩段檢查鎖機制(在上述代碼的彙編代碼中,可以清楚地看到兩段檢查鎖的邏輯)。換言之,C++11對於static變量,自帶使用了單例模式,自然不用我們再費事。
#include <iostream>
#include <vector>
#include <thread>
using namespace std;

class singleton{
public:
    static singleton* getInstance() {
        static singleton ptrST_;
        return &ptrST_;
    }
    void print() {
        cout << "print is called" << endl;
    }
private:
    singleton (){
        cout << "singleton is called" << endl;
    }
};

void func() {
    auto ptr = singleton::getInstance();
    ptr->print();
}

int main() {
    vector<thread> threadPool;
    for(int i = 0; i < 10; i++) {
        threadPool.push_back(thread(func));
    }
    for(auto &p: threadPool) {
        p.join();
    }
    return 0;
}
//輸出
singleton is called
print is called
print is called
print is called
print is called
print is called
print is called
print is called
print is called
print is called
print is called
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章