C++之特殊類設計

文章目錄

  • 前言
  • 一、 設計一個不能被拷貝的類
    • 1. C++98 實現方式
    • 2. C++11 實現方式
  • 二、設計一個只能在堆上創建對象的類
    • 1. 方法一:析構函數私有,提供destory接口釋放資源
    • 2. 方法二:構造函數私有
  • 三、 設計一個只能在棧上創建對象的類
    • 1. 實現方式
  • 四、設計一個不能被繼承的類
    • 1. C++98 實現方式
    • 2. C++11 實現方式
  • 五、設計一個只能創建一個對象(單例模式)
    • 1. 單例模式介紹
    • 2. 餓漢模式(Eager Singleton)
      • (1)餓漢模式的實現步驟
      • (2)代碼解析
      • (3)餓漢模式的優缺點
    • 3. 懶漢模式(Lazy Singleton)
      • (1)為什么使用懶漢模式?
      • (2)代碼解析
      • (4)防止拷貝
      • (5)對象持久化
    • 4. 餓漢VS懶漢
  • 總結


前言

今天我們一起來學習常見的特殊類怎么設計,以及了解什么是單例~


一、 設計一個不能被拷貝的類

拷貝發生在兩種場景:拷貝構造函數和賦值運算符。因此,若要禁止拷貝,只需讓該類不能調用這兩個函數。

1. C++98 實現方式

  • 方法:將拷貝構造函數和賦值運算符 僅聲明不定義,并設置為 private
  • 原因
    1. 私有化拷貝構造和賦值運算符,防止外部訪問。
    2. 僅聲明不定義,確保該函數不會被調用,即使成員函數內部嘗試拷貝也會報錯。
class CopyBan {
private:CopyBan(const CopyBan&);CopyBan& operator=(const CopyBan&);
};

2. C++11 實現方式

  • C++11 提供 = delete 語法,顯式刪除默認拷貝構造和賦值運算符。
class CopyBan {
public:CopyBan(const CopyBan&) = delete;CopyBan& operator=(const CopyBan&) = delete;
};

二、設計一個只能在堆上創建對象的類

1. 方法一:析構函數私有,提供destory接口釋放資源

class HeapOnly
{
public:void Destroy(){delete this;}
private:~HeapOnly(){//...}
};int main()
{//HeapOnly hp1;//static HeapOnly hp2;HeapOnly* hp3 = new HeapOnly;//delete hp3;hp3->Destroy();return 0;
}

2. 方法二:構造函數私有

步驟:

  1. 構造函數私有
  2. 提供靜態成員函數創建對象
  3. 禁拷貝與賦值,防止利用拷貝創建棧上的對象

具體代碼和使用如下:

class HeapOnly
{
public:static HeapOnly* CreateObj(){return new HeapOnly;}
private:HeapOnly(){//...}HeapOnly(const HeapOnly& hp) = delete;HeapOnly& operator=(const HeapOnly& hp) = delete;
};int main()
{//HeapOnly hp1;//static HeapOnly hp2;//HeapOnly* hp3 = new HeapOnly;HeapOnly* hp3 = HeapOnly::CreateObj();//HeapOnly copy(*hp3);return 0;
}

三、 設計一個只能在棧上創建對象的類

1. 實現方式

步驟:

  1. 類比只能在堆上創建對象的類,先將構造函數私有
  2. 提供靜態成員函數構造,不同的是只能在堆上創建對象的類new來創造,這里傳值返回
  3. 為了避免這種情況:StackOnly* copy = new StackOnly(s),這樣的拷貝方式創建堆上的對象,但是我們又不能禁拷貝和賦值,因此需要禁掉operator new,這樣來寫void* operator new(size_t) = delete

具體代碼如下:

class StackOnly
{
public:static StackOnly CreateObj(){StackOnly st;return st;}
private:StackOnly(){//...}// 對一個類實現專屬operator newvoid* operator new(size_t size) = delete;
};int main()
{//StackOnly hp1;//static StackOnly hp2;//StackOnly* hp3 = new StackOnly;StackOnly hp3 = StackOnly::CreateObj();StackOnly copy(hp3);//  new  operator new + 構造// StackOnly* hp4 = new StackOnly(hp3);return 0;
}

四、設計一個不能被繼承的類

1. C++98 實現方式

  • 方法:將構造函數 private,防止繼承。
class NonInherit {
public:static NonInherit GetInstance() {return NonInherit();}private:NonInherit() {}
};

2. C++11 實現方式

  • 方法:使用 final 關鍵字。
class A final {// 該類無法被繼承
};

五、設計一個只能創建一個對象(單例模式)

1. 單例模式介紹

  • 保證系統中某個類只有一個實例
  • 提供全局訪問點
  • 應用場景:如全局配置管理。

2. 餓漢模式(Eager Singleton)

  • 特點:程序啟動時即創建實例。(main函數之前就創建)
  • 優點:線程安全。
  • 缺點:可能導致啟動慢。

(1)餓漢模式的實現步驟

代碼:

namespace hungry
{class Singleton{public:// 2、提供獲取單例對象的接口函數, 返回靜態成員變量static Singleton& GetInstance(){return _sinst; }void func();void Add(const pair<string, string>& kv){_dict[kv.first] = kv.second;}void Print(){for (auto& e : _dict){cout << e.first << ":" << e.second << endl;}cout << endl;}private:// 1、構造函數私有,防止外部直接創建對象Singleton() {}// 3、防拷貝:刪除拷貝構造和賦值運算符,避免創建多個實例Singleton(const Singleton& s) = delete;Singleton& operator=(const Singleton& s) = delete;map<string, string> _dict;// 4、靜態成員變量,在程序啟動時就初始化static Singleton _sinst;};// 5、在類外部定義靜態實例,在main函數執行前已創建Singleton Singleton::_sinst;
}

(2)代碼解析

(1)構造函數私有化

Singleton() {}
  • 目的是防止外部代碼通過 new 關鍵字創建對象,確保 Singleton 類只能在 GetInstance() 方法中創建實例。

(2)提供靜態方法 GetInstance()

static Singleton& GetInstance()
{return _sinst;
}
  • 通過靜態方法返回單例對象的引用,確保所有地方訪問的都是同一個實例。

(3)防拷貝

Singleton(const Singleton& s) = delete;
Singleton& operator=(const Singleton& s) = delete;
  • 防止拷貝和賦值,避免創建多個 Singleton 實例。

(4)靜態成員變量 _sinst

static Singleton _sinst;
  • 程序啟動時(main() 執行前)就創建該實例,無論是否真的需要。

(3)餓漢模式的優缺點

? 優點

  1. 線程安全

    • 靜態成員 _sinst 在編譯時創建,天然是線程安全的,無需額外同步措施(如 mutex)。
  2. 實現簡單

    • 不需要加鎖,避免了懶漢模式的 double-check 加鎖復雜性。
  3. 訪問速度快

    • 由于實例在程序啟動時就已經創建,訪問時無延遲,直接返回。

? 缺點

  1. 浪費資源

    • 如果該單例對象初始化內容很多,而程序運行期間根本沒用到,就會浪費資源,降低程序啟動速度。
  2. 難以控制對象創建順序

    • 如果多個單例對象存在依賴關系(如 A 依賴 B),可能會導致未定義行為
    • 例如:
      class A {static A a_instance;B b; // A 依賴 B
      };class B {static B b_instance;A a; // B 依賴 A
      };
      
      • 由于 _sinst 在編譯期靜態初始化,兩個類的創建順序是由編譯器決定的,可能會出現 A 還未初始化,但 B 已經嘗試訪問 A 的問題。

3. 懶漢模式(Lazy Singleton)

  • 特點:第一次使用時創建實例。
  • 優點:啟動快,資源按需分配。
  • 缺點:線程不安全,需要加鎖。

懶漢模式(Lazy Singleton)的實現思路解析
懶漢模式是一種 單例模式(Singleton Pattern)的實現方式,其特點是 延遲創建實例,即第一次使用時才創建對象,而不是程序啟動時就初始化(像餓漢模式那樣)。


(1)為什么使用懶漢模式?

懶漢模式的主要優點是延遲加載,適用于 對象創建成本較高、但并不是一定會用到的情況,比如:

  • 數據庫連接
  • 日志管理
  • 需要動態管理生命周期的單例(如緩存數據)

(2)代碼解析

完整代碼:

namespace lazy
{class Singleton{public:// 2、提供獲取單例對象的接口函數static Singleton& GetInstance(){if (_psinst == nullptr){// 第一次調用 GetInstance 時創建單例對象_psinst = new Singleton;}return *_psinst;}// 釋放單例對象(用于手動釋放或持久化數據)static void DelInstance(){if (_psinst){delete _psinst;_psinst = nullptr;}}void Add(const pair<string, string>& kv){_dict[kv.first] = kv.second;}void Print(){for (auto& e : _dict){cout << e.first << ":" << e.second << endl;}cout << endl;}// 3、GC(垃圾回收)類,在程序結束時自動釋放 Singletonclass GC{public:~GC(){lazy::Singleton::DelInstance();}};private:// 1、構造函數私有,防止外部直接創建對象Singleton(){// ...}~Singleton(){cout << "~Singleton()" << endl;// map數據寫到文件中(持久化)FILE* fin = fopen("map.txt", "w");for (auto& e : _dict){fputs(e.first.c_str(), fin);fputs(":", fin);fputs(e.second.c_str(), fin);fputs("\n", fin);}fclose(fin);}// 4、防拷貝Singleton(const Singleton& s) = delete;Singleton& operator=(const Singleton& s) = delete;map<string, string> _dict;static Singleton* _psinst; // 指向單例對象的指針static GC _gc; // 靜態 GC 對象,自動釋放 Singleton};// 5、靜態成員變量初始化Singleton* Singleton::_psinst = nullptr; // 初始化單例指針為空Singleton::GC Singleton::_gc; // 在程序退出時自動釋放 Singleton
}int main()
{cout << &lazy::Singleton::GetInstance() << endl;cout << &lazy::Singleton::GetInstance() << endl;cout << &lazy::Singleton::GetInstance() << endl;// 添加數據lazy::Singleton::GetInstance().Add({ "xxx", "111" });lazy::Singleton::GetInstance().Add({ "yyy", "222" });lazy::Singleton::GetInstance().Add({ "zzz", "333" });lazy::Singleton::GetInstance().Add({ "abc", "333" });// 打印數據lazy::Singleton::GetInstance().Print();// 修改數據lazy::Singleton::GetInstance().Add({ "abc", "444" });lazy::Singleton::GetInstance().Print();// 不手動調用 DelInstance,程序結束時 GC 自動釋放return 0;
}

(1)懶加載(Lazy Initialization)

static Singleton& GetInstance()
{if (_psinst == nullptr){_psinst = new Singleton;}return *_psinst;
}

特點:

  • _psinst 指針初始化為 nullptr,意味著程序啟動時不會創建實例。
  • 首次調用 GetInstance() 時才創建 Singleton 實例
  • 之后每次調用 GetInstance(),返回的都是同一個對象

(2)手動釋放單例
一般來說,單例是不需要去釋放的,

特殊場景:1、中途需要顯示釋放 2、程序結束時,需要做一些特殊動作(如持久化)(GC)

static void DelInstance()
{if (_psinst){delete _psinst;_psinst = nullptr;}
}

作用:

  • 由于 Singleton 對象是動態創建的,所以需要手動釋放。
  • DelInstance() 用于手動釋放對象,當程序需要手動控制資源釋放時可以調用。

(3)GC 機制(自動釋放單例)

class GC
{
public:~GC(){lazy::Singleton::DelInstance();}
};

工作原理:

  • GCSingleton 內部的一個嵌套類。
  • static GC _gc; 是一個 靜態成員變量,它的 析構函數會在程序退出時被調用,從而自動釋放 Singleton 實例。

? 為什么要加 GC

  • 避免 內存泄漏,因為 Singleton 對象是 new 出來的,程序退出時如果不手動 delete,就會發生泄漏。
  • 確保 Singletonmain() 結束時釋放,不會影響其他對象析構的順序。

(4)防止拷貝

Singleton(const Singleton& s) = delete;
Singleton& operator=(const Singleton& s) = delete;
  • 防止 拷貝構造賦值運算符,保證單例模式不被破壞。

(5)對象持久化

~Singleton()
{cout << "~Singleton()" << endl;// map數據寫到文件中FILE* fin = fopen("map.txt", "w");for (auto& e : _dict){fputs(e.first.c_str(), fin);fputs(":", fin);fputs(e.second.c_str(), fin);fputs("\n", fin);}fclose(fin);
}
  • Singleton 的析構函數中,把 _dict 數據寫入文件,確保程序退出時數據不會丟失。

在這里插入圖片描述

在這里插入圖片描述


4. 餓漢VS懶漢

方式線程安全訪問速度資源消耗適用場景
餓漢模式? 安全? 快? 可能浪費頻繁使用的單例對象(如日志、配置管理)
懶漢模式? 需加鎖? 訪問有延遲? 只在需要時創建大量占用資源但不一定用到的單例

總結

到這里就結束啦~
謝謝大家,希望對您有所幫助~

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/96697.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/96697.shtml
英文地址,請注明出處:http://en.pswp.cn/web/96697.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

TupiTube,一款免費開源的 2D 動畫創作工具

TupiTube&#xff0c;一款免費開源的 2D 動畫創作工具 ** ** 功能 ** &#xff1a;開源、免費的 2D 動畫軟件&#xff0c;界面簡單&#xff0c;支持逐幀動畫、剪紙動畫、定格動畫&#xff0c;能導入素材并導出多種視頻和圖片格式&#xff0c;適合兒童、學生和動畫愛好者入門創作…

MoE架構訓練系統設計:專家并行與門控網絡優化策略

點擊 “AladdinEdu&#xff0c;同學們用得起的【H卡】算力平臺”&#xff0c;注冊即送-H卡級別算力&#xff0c;80G大顯存&#xff0c;按量計費&#xff0c;靈活彈性&#xff0c;頂級配置&#xff0c;學生更享專屬優惠。 摘要 混合專家&#xff08;Mixture of Experts&#xf…

使用Python爬蟲,selenium和requests誰更強?

py爬蟲的話&#xff0c;selenium和reqeusts誰更強&#xff0c;selenium是不是能完全取代requests? 答案基本是可以的&#xff0c;selenium適合動態網頁抓取&#xff0c;因為它可以控制瀏覽器去點擊、加載網頁&#xff0c;requests則比較適合靜態網頁采集&#xff0c;它非常輕…

編譯原理-文法壓縮練習

這個任務的目標就是把一個給定的文法變得“干凈”和“高效”&#xff0c;剔除所有無用的部分。根據幻燈片&#xff0c;無用的&#xff08;多余的&#xff09;規則分為兩大類&#xff1a; 不可達規則&#xff1a;規則的“頭”&#xff08;左部非終結符&#xff09;從起始符號出發…

GPU硬件架構和配置的理解

從公司架構理解GPU架構想象一個GPU就像一家大型科技公司&#xff0c;它的任務是處理圖形和計算任務&#xff08;“干活”&#xff09;。硬件概念公司架構比喻作用和特點Platform (平臺)集團公司最大的獨立實體。比如谷歌Alphabet是一個集團公司&#xff0c;它旗下有谷歌、Waymo…

【硬件開發】電源抑制比PSRR

電源抑制比PSRR是電壓輸入量和電壓輸出量的比值&#xff0c;通常用dB來表示。 PSRR這個參數經常和運放&#xff0c;LDO,DCDC變換器有關聯。(2 封私信 / 58 條消息) 電源抑制比(PSRR)的基礎知識 - 知乎

七、卷積神經網絡

目錄 7.1 整體結構 7.2 卷積層 7.2.1 全連接層存在的問題 7.2.2 卷積運算 7.2.3 填充 7.2.5 3維數據的卷積運算 7.2.6 結合方塊思考 7.2.7 批處理 7.3 池化層 7.4 卷積層和池化層的實現 7.4.1 4維數組 7.4.2 基于 im2col的展開 7.4.3 卷積層的實現 7.4.4 池化層的…

加餐加餐!燒烤斗破蒼穹

忽然起了吃燒烤的念頭&#xff0c;便掏出手機點了一堆。不過二十分鐘&#xff0c;外賣小哥便按響了門鈴&#xff0c;手里提著一個方正的紙袋&#xff0c;還冒著熱氣。我將燒烤一一取出&#xff0c;排在茶幾上。肉串油光發亮&#xff0c;韭菜翠綠間點綴著蒜蓉&#xff0c;茄子剖…

搜索引擎收錄網站帶www和不帶www有區別嗎?

這是一個非常常見且重要的問題。簡單直接的回答是&#xff1a;有區別&#xff0c;但對搜索引擎來說&#xff0c;處理得當就不會重復&#xff1b;處理不當則會造成嚴重重復和權重分散。下面我為您詳細解釋一下&#xff0c;并提供正確的處理方法。核心區別&#xff1a;兩個不同的…

AFSim2.9.0學習筆記 —— 2、AFSim的Wizard軟件概述(ArkSIM集成開發環境 (IDE))

&#x1f514; AFSim2.9.0 相關技術、疑難雜癥文章合集&#xff08;掌握后可自封大俠 ?_?&#xff09;&#xff08;記得收藏&#xff0c;持續更新中…&#xff09; 若還沒有下載AFSim2.9.0完整軟件或源碼&#xff0c;請先進入本人另篇文章了解下載。 正文 ??主界面 打開 Ar…

建自己的Python項目倉庫,使用工具:GitHub(遠程倉庫)、GitHub Desktop(版本控制工具)、VSCode(代碼編輯器)

結合 GitHub&#xff08;遠程倉庫&#xff09;、GitHub Desktop&#xff08;版本控制工具&#xff09;、VSCode&#xff08;代碼編輯器&#xff09; 三個工具&#xff0c;以下是更具體的Python項目倉庫搭建流程&#xff0c;包含工具協同操作的詳細步驟&#xff1a; 一、整體流程…

iDEA Lombok 失效 和 slf log 變量失效問題

1. lombok 失效&#xff1a;檢查下配置有沒有使用注解處理器&#xff1b;且這個處理中有沒有帶上版本&#xff1b;版本號需要與上面引入的依賴版本一致。2. 對于找不到 log 變量的操作&#xff0c;則是使用下面將這個變量使用下面的代碼定義出來&#xff1b;上面去掉 slf4j注解…

go資深之路筆記(二) sync.Pool

一、 使用 sync.Pool 減少 GC 壓力&#xff0c;提升性能 簡單講下go的gc&#xff0c;它的核心原理就是三色標記法和寫屏障&#xff0c;可以實現優秀并發處理。gc一般不會頻繁調用&#xff0c;他是根據GOGC的值來判斷&#xff0c;具體就是上次觸發GC后總堆值大于等于上次的(1GO…

【面試筆記-Java開發崗】

目錄&#xff1a;1. synchronized 和 ReentrantLock 的區別及應用場景2. HashMap 與 LinkedHashMap 的區別3. ConcurrentHashMap 的數據結構及 JDK1.7 與 JDK1.8 區別4. Spring 常用的模式及應用場景5. 事務的四大特性&#xff08;ACID&#xff09;6. 鎖機制&#xff1a;行級鎖…

CSS :has() 選擇器詳解:為什么它是“父選擇器”?如何實現真正的容器查詢?

一、前言 在傳統的 CSS 中&#xff0c;我們只能根據元素的自身屬性、類名、ID 或其子元素/兄弟元素來設置樣式&#xff0c;卻無法根據其父元素或后代元素的狀態來改變自身樣式。 直到 :has() 選擇器的出現&#xff0c;這一局面被徹底改變。 :has() 被稱為 “父選擇器” 或 “…

李宏毅 Deep Learning

感謝李宏毅老師qwq1. 基礎概念1.1 Machine Learning問題引出&#xff1a;預測后面幾天的觀看人數&#xff1b;初步構建模型&#xff1a;擬合效果不好&#xff0c;就是在原數據上平移了一段距離&#xff1b;此處構建模型的本質&#xff1a;利用特征工程&#xff0c;將“多維特征…

【AI論文】分享即關愛:基于集體強化學習經驗共享的高效語言模型(LM)后訓練方法

摘要&#xff1a;利用強化學習&#xff08;RL&#xff09;對語言模型&#xff08;LMs&#xff09;進行后訓練&#xff0c;無需監督微調即可增強其復雜推理能力&#xff0c;DeepSeek-R1-Zero便證明了這一點。然而&#xff0c;要有效利用強化學習訓練語言模型&#xff0c;需要進行…

工業網關在汽車沖壓車間的應用:EtherNet/IP轉EtherCAT集成實踐

在汽車零部件沖壓車間中&#xff0c;生產線的高效協同與精準控制是提升整體產能的關鍵。隨著自動化設備的多樣化&#xff0c;不同協議的設備之間的通信成為技術難點。例如&#xff0c;羅克韋爾PLC通常采用EtherNet/IP協議&#xff0c;而許多高性能機械臂則依賴EtherCAT協議。如…

【底層機制】【C++】std::move 為什么引入?是什么?怎么實現的?怎么正確用?

C++底層機制推薦閱讀 【C++基礎知識】深入剖析C和C++在內存分配上的區別 【底層機制】【C++】vector 為什么等到滿了才擴容而不是提前擴容? 【底層機制】malloc 在實現時為什么要對大小內存采取不同策略? 【底層機制】剖析 brk 和 sbrk的底層原理 【底層機制】為什么棧的內存…

Redis面試相關

數據過期策略 惰性刪除 當用到那個key的時候再檢查是否過期&#xff0c;過期則刪除&#xff0c;有效則返回key 優點是可以節省檢查過期的時間 缺點是會浪費內存 定期刪除 每隔一段時間對一些key進行檢查并且刪除里面的過期key 有兩種模式 slow模式是定時任務&#xff0c;頻率是…