您好,登錄后才能下訂單哦!
這篇文章主要介紹“C++特殊類設計概念是什么”,在日常操作中,相信很多人在C++特殊類設計概念是什么問題上存在疑惑,小編查閱了各式資料,整理出簡單好用的操作方法,希望對大家解答”C++特殊類設計概念是什么”的疑惑有所幫助!接下來,請跟著小編一起來學習吧!
設計模式是一套被反復使用、多數人知曉的、經過分類的、代碼設計經驗的總結。
使用設計模式的目的:為了代碼可重用性、讓代碼更容易被他人理解、保證代碼可靠性。
根本原因是為了代碼復用,增加可維護性。
設計模式的例子:迭代器模式
拷貝一共就只有兩個場景,一個是拷貝構造,一個是賦值運算符重載。所以我們想要設計出一個不能被拷貝的類只需要讓外部無法調用這兩個函數即可。
在C++98中,我們的方法是將拷貝構造和賦值運算符重載只聲明不定義并且將權限設置為私有。
class anti_copy { public: anti_copy() {} private: anti_copy(const anti_copy& ac); anti_copy& operator=(const anti_copy& ac); };
設計原因:
1?? 私有:如果聲明成共有,那么就可以在類外面實現定義。
2?? 只聲明不定義:因為如果不聲明編譯器會默認生成這兩個的默認成員函數。而不定義是因為該函數不會被調用,就不用寫了,這樣編譯的時候就會出現鏈接錯誤。
而在C++11中引入了關鍵字——delete。
如果在默認成員函數后跟上=delete
,表示讓編譯器刪除掉該默認成員函數。即使權限是共有也無法調用已刪除的函數。
class anti_copy { public: anti_copy() {} anti_copy(const anti_copy& ac) = delete; anti_copy& operator=(const anti_copy& ac) = delete; private: };
首先要把構造函數給私有,不然這個類就可以在任意位置被創建。而構造函數被私有了以后我們怎么創建對象呢?
我們可以在定義一個成員函數,讓這個函數在堆上申請空間,但我們知道必須現有對象才能調用成員函數。所以我們就把這個函數設置成靜態成員函數。
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就是棧上的對象。所以我們也要把拷貝構造給封住。
class OnlyHeap { public: static OnlyHeap* GetObj() { return new OnlyHeap; } OnlyHeap(const OnlyHeap& hp) = delete; private: OnlyHeap() {} };
class OnlyHeap { public: OnlyHeap() {} OnlyHeap(const OnlyHeap& hp) = delete; private: ~OnlyHeap() {} }; int main() { OnlyHeap hp1;// error OnlyHeap* hp2 = new OnlyHeap; return 0; }
這里的hp1就不能創建成功,因為對象銷毀的時候會調用析構函數,但是這里的析構是私有的,所以該對象無法調用。
但是我們要銷毀hp2該怎么辦呢?
我們可以定義一個成員函數顯示調用析構函數。
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; }
為了不讓這個類隨便定義出對象,首先要把構造函數私有。然后跟上面只能在堆上創建對象的方法相似,定義出一個靜態成員函數返回棧上創建的對象。
class StackOnly { public: static StackOnly GetObj() { return StackOnly(); } private: StackOnly() {} }; int main() { StackOnly hp = StackOnly::GetObj(); return 0; }
但是這里有一個問題,無法防止創建靜態對象:
static StackOnly hp2 = StackOnly::GetObj();
在C++98,為了不讓子類繼承,我們可以把構造函數私有化,因為子類需要先調用父類的構造函數初始化父類的那一部分成員。
class NoInherit { public: private: NoInherit() {} };
而在C++11中引入的新的關鍵字final
,被final
關鍵字修飾的類不能被繼承。
class NoInherit final { public: private: };
一個類只能創建一個對象,即單例模式,該模式可以保證系統中該類只有一個實例,并提供一個訪問它的全局訪問點,該實例被所有程序模塊共享。
單例模式的特點就是全局只有一個唯一對象。
怎么能做到全局只是用一個對象呢,比方說我們現在想要實現一個英漢字典,首先我們要把構造函數私有,不然無法阻止創建對象。然后我們可以在類里面定義一個自己類型的靜態成員變量,作用域是全局的。因為對比定義在外邊的靜態成員變量,內部的可以調用構造函數。
這里要注意把拷貝也要封住。
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之前)就創建對象。
餓漢模式有什么缺點呢?
1?? 如果單例對象構造十分耗時或者占用很多資源,比如加載插件啊, 初始化網絡連接啊,讀取文件啊等等,而有可能該對象程序運行時不會用到,那么也要在程序一開始就進行初始化,就會導致程序啟動時非常的緩慢。
2?? 多個單例類之間如果有依賴關系餓漢模式就無法控制,比方說要求A類初始化時必須調用B,但是餓漢無法控制先后順序。
所以針對這些問題,就有了懶漢模式。
第一次使用實例對象時,創建對象(用的時候創建)。進程啟動無負載。多個單例實例啟動順序自由控制。
我們可以直接對上面餓漢模式的代碼進行修改,把靜態成員變量變成指針。然后把獲取的函數改變一下:
static Singleton& GetObj() { // 第一次調用才會創建對象 if (_s == nullptr) { _s = new Singleton; } return *_s; }
整體代碼:
class Singleton { public: static Singleton& GetObj() { // 第一次調用才會創建對象 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;// 定義
上面的代碼存在問題,當多個線程同時調用GetObj(),就會創建多個對象。所以為了線程安全我們要加鎖。為了保證鎖自動銷毀,我們可以自定義一個鎖。
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() { // 第一次調用才會創建對象 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;// 定義
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; };
這里就用了靜態局部變量只會在第一次定義的時候初始化。在C++11之前是不能保證線程安全的,但是C++11之后就可以了。
到此,關于“C++特殊類設計概念是什么”的學習就結束了,希望能夠解決大家的疑惑。理論與實踐的搭配能更好的幫助大家學習,快去試試吧!若想繼續學習更多相關知識,請繼續關注億速云網站,小編會繼續努力為大家帶來更多實用的文章!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。