C++特殊類設(shè)計概念與示例講解
一、設(shè)計模式概念
設(shè)計模式是一套被反復(fù)使用、多數(shù)人知曉的、經(jīng)過分類的、代碼設(shè)計經(jīng)驗的總結(jié)。
使用設(shè)計模式的目的:為了代碼可重用性、讓代碼更容易被他人理解、保證代碼可靠性。
根本原因是為了代碼復(fù)用,增加可維護(hù)性。
設(shè)計模式的例子:迭代器模式
二、設(shè)計一個不能被拷貝的類
拷貝一共就只有兩個場景,一個是拷貝構(gòu)造,一個是賦值運算符重載。所以我們想要設(shè)計出一個不能被拷貝的類只需要讓外部無法調(diào)用這兩個函數(shù)即可。
在C++98中,我們的方法是將拷貝構(gòu)造和賦值運算符重載只聲明不定義并且將權(quán)限設(shè)置為私有。
class anti_copy { public: anti_copy() {} private: anti_copy(const anti_copy& ac); anti_copy& operator=(const anti_copy& ac); };
設(shè)計原因:
1?? 私有:如果聲明成共有,那么就可以在類外面實現(xiàn)定義。
2?? 只聲明不定義:因為如果不聲明編譯器會默認(rèn)生成這兩個的默認(rèn)成員函數(shù)。而不定義是因為該函數(shù)不會被調(diào)用,就不用寫了,這樣編譯的時候就會出現(xiàn)鏈接錯誤。
而在C++11中引入了關(guān)鍵字——delete。
如果在默認(rèn)成員函數(shù)后跟上=delete
,表示讓編譯器刪除掉該默認(rèn)成員函數(shù)。即使權(quán)限是共有也無法調(diào)用已刪除的函數(shù)。
class anti_copy { public: anti_copy() {} anti_copy(const anti_copy& ac) = delete; anti_copy& operator=(const anti_copy& ac) = delete; private: };
三、設(shè)計一個只能在堆上創(chuàng)建對象的類
3.1 私有構(gòu)造
首先要把構(gòu)造函數(shù)給私有,不然這個類就可以在任意位置被創(chuàng)建。而構(gòu)造函數(shù)被私有了以后我們怎么創(chuàng)建對象呢?
我們可以在定義一個成員函數(shù),讓這個函數(shù)在堆上申請空間,但我們知道必須現(xiàn)有對象才能調(diào)用成員函數(shù)。所以我們就把這個函數(shù)設(shè)置成靜態(tài)成員函數(shù)。
class OnlyHeap { public: static OnlyHeap* GetObj() { return new OnlyHeap; } private: OnlyHeap() {} };
但是這樣也不完全對,如果我們這么寫:
class OnlyHeap { public: static OnlyHeap* GetObj() { return new OnlyHeap; } private: OnlyHeap() {} }; int main() { OnlyHeap* hp1 = OnlyHeap::GetObj(); OnlyHeap hp2(*hp1); return 0; }
這里的hp2就是棧上的對象。所以我們也要把拷貝構(gòu)造給封住。
class OnlyHeap { public: static OnlyHeap* GetObj() { return new OnlyHeap; } OnlyHeap(const OnlyHeap& hp) = delete; private: OnlyHeap() {} };
3.2 私有析構(gòu)
class OnlyHeap { public: OnlyHeap() {} OnlyHeap(const OnlyHeap& hp) = delete; private: ~OnlyHeap() {} }; int main() { OnlyHeap hp1;// error OnlyHeap* hp2 = new OnlyHeap; return 0; }
這里的hp1就不能創(chuàng)建成功,因為對象銷毀的時候會調(diào)用析構(gòu)函數(shù),但是這里的析構(gòu)是私有的,所以該對象無法調(diào)用。
但是我們要銷毀hp2該怎么辦呢?
我們可以定義一個成員函數(shù)顯示調(diào)用析構(gòu)函數(shù)。
class OnlyHeap { public: OnlyHeap() {} OnlyHeap(const OnlyHeap& hp) = delete; void Destroy() { this->~OnlyHeap(); } private: ~OnlyHeap() {} }; int main() { OnlyHeap* hp2 = new OnlyHeap; hp2->Destroy(); return 0; }
四、設(shè)計一個只能在棧上創(chuàng)建對象的類
為了不讓這個類隨便定義出對象,首先要把構(gòu)造函數(shù)私有。然后跟上面只能在堆上創(chuàng)建對象的方法相似,定義出一個靜態(tài)成員函數(shù)返回棧上創(chuàng)建的對象。
class StackOnly { public: static StackOnly GetObj() { return StackOnly(); } private: StackOnly() {} }; int main() { StackOnly hp = StackOnly::GetObj(); return 0; }
但是這里有一個問題,無法防止創(chuàng)建靜態(tài)對象:
static StackOnly hp2 = StackOnly::GetObj();
五、設(shè)計不能被繼承的類
在C++98,為了不讓子類繼承,我們可以把構(gòu)造函數(shù)私有化,因為子類需要先調(diào)用父類的構(gòu)造函數(shù)初始化父類的那一部分成員。
class NoInherit { public: private: NoInherit() {} };
而在C++11中引入的新的關(guān)鍵字final
,被final
關(guān)鍵字修飾的類不能被繼承。
class NoInherit final { public: private: };
六、單例模式
一個類只能創(chuàng)建一個對象,即單例模式,該模式可以保證系統(tǒng)中該類只有一個實例,并提供一個訪問它的全局訪問點,該實例被所有程序模塊共享。
單例模式的特點就是全局只有一個唯一對象。
6.1 餓漢模式
怎么能做到全局只是用一個對象呢,比方說我們現(xiàn)在想要實現(xiàn)一個英漢字典,首先我們要把構(gòu)造函數(shù)私有,不然無法阻止創(chuàng)建對象。然后我們可以在類里面定義一個自己類型的靜態(tài)成員變量,作用域是全局的。因為對比定義在外邊的靜態(tài)成員變量,內(nèi)部的可以調(diào)用構(gòu)造函數(shù)。
這里要注意把拷貝也要封住。
class Singleton { public: static Singleton& GetObj() { return _s; } void insert(const std::string& s1, const std::string& s2) { _dict[s1] = s2; } void Print() { for (auto& e : _dict) { cout << e.first << "->" << e.second << endl; } } // 防拷貝 Singleton(const Singleton&) = delete; Singleton& operator=(const Singleton&) = delete; private: Singleton() {} std::map<std::string, std::string> _dict; private: static Singleton _s;// 聲明 }; Singleton Singleton::_s;// 定義 int main() { Singleton::GetObj().insert("corn", "玉米"); Singleton& dic1 = Singleton::GetObj(); dic1.insert("apple", "蘋果"); dic1.insert("banana", "香蕉"); Singleton& dic2 = Singleton::GetObj(); dic2.insert("pear", "梨"); dic2.Print(); return 0; }
餓漢模式有什么特點呢?
它會在一開始(main之前)就創(chuàng)建對象。
餓漢模式有什么缺點呢?
1?? 如果單例對象構(gòu)造十分耗時或者占用很多資源,比如加載插件啊, 初始化網(wǎng)絡(luò)連接啊,讀取文件啊等等,而有可能該對象程序運行時不會用到,那么也要在程序一開始就進(jìn)行初始化,就會導(dǎo)致程序啟動時非常的緩慢。
2?? 多個單例類之間如果有依賴關(guān)系餓漢模式就無法控制,比方說要求A類初始化時必須調(diào)用B,但是餓漢無法控制先后順序。
所以針對這些問題,就有了懶漢模式。
6.2 懶漢模式
第一次使用實例對象時,創(chuàng)建對象(用的時候創(chuàng)建)。進(jìn)程啟動無負(fù)載。多個單例實例啟動順序自由控制。
我們可以直接對上面餓漢模式的代碼進(jìn)行修改,把靜態(tài)成員變量變成指針。然后把獲取的函數(shù)改變一下:
static Singleton& GetObj() { // 第一次調(diào)用才會創(chuàng)建對象 if (_s == nullptr) { _s = new Singleton; } return *_s; }
整體代碼:
class Singleton { public: static Singleton& GetObj() { // 第一次調(diào)用才會創(chuàng)建對象 if (_s == nullptr) { _s = new Singleton; } return *_s; } void insert(const std::string& s1, const std::string& s2) { _dict[s1] = s2; } void Print() { for (auto& e : _dict) { cout << e.first << "->" << e.second << endl; } } // 防拷貝 Singleton(const Singleton&) = delete; Singleton& operator=(const Singleton&) = delete; private: Singleton() {} std::map<std::string, std::string> _dict; private: static Singleton* _s;// 聲明 }; Singleton* Singleton::_s = nullptr;// 定義
6.2.1 線程安全問題
上面的代碼存在問題,當(dāng)多個線程同時調(diào)用GetObj(),就會創(chuàng)建多個對象。所以為了線程安全我們要加鎖。為了保證鎖自動銷毀,我們可以自定義一個鎖。
template <class Lock> class LockAuto { public: LockAuto(Lock& lk) : _lk(lk) { _lk.lock(); } ~LockAuto() { _lk.unlock(); } private: Lock& _lk; }; class Singleton { public: static Singleton& GetObj() { // 第一次調(diào)用才會創(chuàng)建對象 if (_s == nullptr)// 只有第一次才用加鎖 { LockAuto<mutex> lock(_mutex); if (_s == nullptr) { _s = new Singleton; } } return *_s; } void insert(const std::string& s1, const std::string& s2) { _dict[s1] = s2; } void Print() { for (auto& e : _dict) { cout << e.first << "->" << e.second << endl; } } // 防拷貝 Singleton(const Singleton&) = delete; Singleton& operator=(const Singleton&) = delete; private: Singleton() {} std::map<std::string, std::string> _dict; private: static Singleton* _s;// 聲明 static mutex _mutex;// 鎖 }; Singleton* Singleton::_s = nullptr;// 定義 mutex Singleton::_mutex;// 定義
6.2.2 新寫法
class Singleton { public: static Singleton& GetObj() { static Singleton dic; return dic; } void insert(const std::string& s1, const std::string& s2) { _dict[s1] = s2; } void Print() { for (auto& e : _dict) { cout << e.first << "->" << e.second << endl; } } // 防拷貝 Singleton(const Singleton&) = delete; Singleton& operator=(const Singleton&) = delete; private: Singleton() {} std::map<std::string, std::string> _dict; };
這里就用了靜態(tài)局部變量只會在第一次定義的時候初始化。在C++11之前是不能保證線程安全的,但是C++11之后就可以了。
到此這篇關(guān)于C++特殊類設(shè)計概念與示例講解的文章就介紹到這了,更多相關(guān)C++特殊類設(shè)計內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
C++的cout.tellp()和cout.seekp()語法介紹
無論是使用 cout 輸出普通數(shù)據(jù),用 cout.put() 輸出指定字符,還是用 cout.write() 輸出指定字符串,數(shù)據(jù)都會先放到輸出流緩沖區(qū),待緩沖區(qū)刷新,數(shù)據(jù)才會輸出到指定位置,本文給大家介紹一下C++的cout.tellp()和cout.seekp()語法,需要的朋友可以參考下2023-09-09使用C++模擬實現(xiàn)2024春晚劉謙魔術(shù)
劉謙在2024年春晚上的撕牌魔術(shù)的數(shù)學(xué)原理非常簡單,所以這篇文章主要為大家詳細(xì)介紹了如何使用C++模擬實現(xiàn)這一魔術(shù)效果,感興趣的可以了解下2024-02-02C/C++編程判斷String字符串是否包含某個字符串實現(xiàn)示例
這篇文章主要為大家介紹了C++編程中判斷String字符串是否包含某個字符串的實現(xiàn)示例,有需要的朋友可以借鑒參考下,希望能夠有所幫助2021-11-11C語言輪轉(zhuǎn)數(shù)組的三種實現(xiàn)
輪轉(zhuǎn)數(shù)組是一種將數(shù)組元素循環(huán)移動的處理方式,它通常用于解決一些需要對固定長度的數(shù)組進(jìn)行循環(huán)滾動處理的問題,本文就介紹了C語言輪轉(zhuǎn)數(shù)組的三種實現(xiàn),感興趣的可以了解一下2023-08-08基于C語言實現(xiàn)簡單的12306火車售票系統(tǒng)
火車售票系統(tǒng)給我們的出行帶來了極大的方面,那么他基于編程是如何實現(xiàn)的呢?今天小編抽時間給大家分享一個使用C語言寫的一個簡單的火車票系統(tǒng),感興趣的朋友參考下2016-09-09