C++ Primer 學習筆記_24_類與資料抽象(10)--static 與單例模式、auto_ptr與單例模式、const成員函數、const 對象、mutable修飾符,constmutable
C++ Primer 學習筆記_24_類與資料抽象(10)--static 與單例模式、auto_ptr與單例模式、const成員函數、const 對象、mutable修飾符
前言
【例】寫出物件導向的五個基本原則?
解答:單一職責原則,開放封閉原則,依賴倒置原則,介面隔離原則和裡氏替換原則
裡氏替換原則:子類型必須能夠替換他們的基底類型。
設計模式分為三種類型:建立型模式、結構型模式和行為型模式
一、static 與單例模式
1、單例模式
單例模式的意圖:保證一個類僅有一個執行個體,並提供一個訪問它的全域訪問點。
(1)第一種形式
#include <iostream>#include <string>using namespace std;class Singleton{private: static Singleton s; int i; Singleton(int x): i(x) {cout << "Singleton" << endl;} Singleton & operator = (Singleton&); //不允許賦值 Singleton(const Singleton&); //不允許拷貝public: static Singleton& instance() {return s;} int getValue() {return i;} void setValue(int x) {i = x;}};Singleton Singleton::s(47); //定義靜態成員sint main(){ Singleton& s = Singleton::instance(); cout << s.getValue() << endl; Singleton& s2 = Singleton::instance(); s2.setValue(9); cout << s.getValue() << endl; return 0;}運行結果:
Singleton
47
9
上述實現通過返回一個引用實現單列模式。如果返回的是一個指標而不是引用,使用者可能會不小心刪除此指標,因此上述實現比返回指標更安全。
(2)第二種形式
#include <iostream>#include <string>using namespace std;class Singleton{private: int i; Singleton(int x): i(x) {cout << "Singleton" << endl;} void operator=(Singleton&); Singleton(const Singleton&); //不允許拷貝public: static Singleton& instance() { static Singleton s(47); return s; } int getValue() {return i;} void setValue(int x) {i = x;}};int main(){ Singleton& s = Singleton::instance(); cout << s.getValue() << endl; Singleton& s2 = Singleton::instance(); s2.setValue(9); cout << s.getValue() << endl; return 0;}運行結果:
Singleton
47
9
上述方法時通過成員函數內部的靜態對象的建立實現單例模式。
注意上述兩種方法並未考慮安全執行緒的問題,如需應用在多線程環境下,需要加鎖。
【例子:禁止拷貝,賦值,預設建構函式建立對象操作】
#include <iostream>using namespace std;class Singleton{public: static Singleton *GetInstance() { if (instance_ == NULL) { instance_ = new Singleton; } return instance_; } ~Singleton() { cout << "~Singleton ..." << endl; }private: Singleton(const Singleton &other); //將拷貝函數放在private,禁止拷貝 Singleton &operator=(const Singleton &other); //禁止賦值 Singleton() //禁止建立對象 { cout << "Singleton ..." << endl; } static Singleton *instance_;};Singleton *Singleton::instance_;int main(void){ //Singleton s1; // Error,調用預設建構函式 Singleton *s1 = Singleton::GetInstance(); Singleton *s2 = Singleton::GetInstance(); //Singleton s3(*s1); // Error,調用拷貝建構函式 return 0;}運行結果:Singleton ...
上述程式雖然調用了兩個GetInstance函數,但只調用一次建構函式,即建立一個對象。將賦值運算子和拷貝建構函式聲明為私人,禁止拷貝。但程式存在一個問題就是物件存留期到時不會被析構。
2、為瞭解決對象不會被析構的問題,可以使用一個靜態嵌套類對象來解決:
#include <iostream>using namespace std;class Singleton{public: static Singleton *GetInstance() { if (instance_ == NULL) { instance_ = new Singleton; } return instance_; } ~Singleton() { cout << "~Singleton ..." << endl; } class Garbo { public: ~Garbo() { if (Singleton::instance_ != NULL) { delete instance_; } } };private: Singleton(const Singleton &other); //將拷貝函數放在private,禁止拷貝 Singleton &operator=(const Singleton &other); //禁止賦值 Singleton() //禁止建立對象 { cout << "Singleton ..." << endl; } static Singleton* instance_; static Garbo garbo_; // 利用對象的確定性析構};Singleton::Garbo Singleton::garbo_;Singleton* Singleton::instance_;int main(void){ //Singleton s1; // Error,調用預設建構函式 Singleton *s1 = Singleton::GetInstance(); Singleton *s2 = Singleton::GetInstance(); //Singleton s3(*s1); // Error,調用拷貝建構函式 return 0;}運行結果:Singleton ...
~Singleton ...
解釋:利用靜態嵌套對象的確定性析構會調用Garbo類的解構函式,在解構函式內delete 單例類的指標。
3、上面辦法比較繁瑣,也可以返回局部靜態對象的引用來解決:
#include <iostream>using namespace std;class Singleton{public: static Singleton& GetInstance() { static Singleton instance; // 局部靜態對象 return instance; } ~Singleton() { cout << "~Singleton ..." << endl; }private: Singleton(const Singleton &other); //將拷貝函數放在private,禁止拷貝 Singleton &operator=(const Singleton &other); //禁止賦值 Singleton() //禁止建立對象 { cout << "Singleton ..." << endl; }};int main(void){ Singleton& s1 = Singleton::GetInstance(); Singleton& s2 = Singleton::GetInstance(); return 0;}運行結果:Singleton ...
~Singleton ...
解釋:局部靜態對象只會初始化一次,所以調用多次GetInstance函數得到的是同一個對象。由於函數內使用了靜態對象,故不是安全執行緒的。
4、實際上也可以使用auto_ptr 智能指標來解決,程式如下,更詳細的對auto_ptr將在後續討論。
#include <iostream>#include<memory>using namespace std;class Singleton{public: static Singleton *GetInstance() { if (instance_.get() == NULL) { instance_ = auto_ptr<Singleton>(new Singleton); } return instance_.get(); } ~Singleton() { cout << "~Singleton ..." << endl; }private: Singleton(const Singleton &other); Singleton &operator=(const Singleton &other); Singleton() { cout << "Singleton ..." << endl; } static auto_ptr<Singleton> instance_;};auto_ptr<Singleton> Singleton::instance_;int main(void){ Singleton *s1 = Singleton::GetInstance(); Singleton *s2 = Singleton::GetInstance(); return 0;}運行結果:
Singleton ...
~Singleton ...
5、餓漢式單例模式(在類載入時就完成了初始化,所以類載入較慢,但擷取對象的速度快)
實際上,上述所有的單例模式例子都不是安全執行緒的,設想如果兩個線程同時運行到語句if (instance == null),而此時該執行個體的確沒有建立,那麼兩個線程都會建立一個執行個體。如果不希望加鎖實現安全執行緒,可以使用餓漢模式(即在main函數之前先產生一個執行個體):
#include <iostream>using namespace std;class Singleton{public: static const Singleton* GetInstance() { return instance_; } ~Singleton() { cout << "~Singleton ..." << endl; } class Garbo { public: ~Garbo() { if (Singleton::instance_ != NULL) { delete instance_; } } };private: Singleton(const Singleton &other); //將拷貝函數放在private,禁止拷貝 Singleton &operator=(const Singleton &other); //禁止賦值 Singleton() //禁止建立對象 { cout << "Singleton ..." << endl; } static const Singleton* instance_; static Garbo garbo_; // 利用對象的確定性析構};const Singleton* Singleton::instance_ = new Singleton();Singleton::Garbo Singleton::garbo_;int main(void){ //Singleton s1; // Error,調用預設建構函式 const Singleton *s1 = Singleton::GetInstance(); const Singleton *s2 = Singleton::GetInstance(); //Singleton s3(*s1); // Error,調用拷貝建構函式 return 0;}
運行結果:
Singleton ...
~Singleton ...
6、或者通過加鎖方式實現,詳細將在後續討論。
二、const成員函數、const 對象、mutable修飾符
1、const 成員函數
const成員函數不會修改對象的狀態
const成員函數只能訪問資料成員的值,而不能修改它
2、const 對象
如果把一個對象指定為const,就是告訴編譯器不要修改它
const對象的定義:
const 類名 對象名(參數表);
const對象不能調用非const成員函數
3、mutable修飾
用mutable修飾的資料成員即使在const對象或在const成員函數中都可以被修改。
#include <iostream>using namespace std;class Test{public: Test(int x) : x_(x), outputTimes_(0) { } int GetX() const { cout << "const GetX ..." << endl; //x_ = 100; //Error,嘗試修改資料成員 return x_; } int GetX() { cout << "GetX ..." << endl; return x_; } void Output() const { cout << "x=" << x_ << endl; outputTimes_++; } int GetOutputTimes() const { return outputTimes_; }private: int x_; mutable int outputTimes_; //mutable修飾};int main(void){ const Test t(10); t.GetX(); Test t2(20); t2.GetX(); t.Output(); t.Output(); cout << t.GetOutputTimes() << endl; return 0;}運行結果:
const GetX ...
GetX ...
x=10
x=10
2
參考:
C++ primer 第四版
Effective C++ 3rd
http://blog.csdn.net/jnu_simba/article/details/9282235
http://blog.csdn.net/zjf280441589/article/details/24704603
C++編程規範
著作權聲明:本文為博主原創文章,未經博主允許不得轉載。