都說 C++ 沒有 GC,RAII: 那麼我算個啥?(贈書福利)

来源:https://www.cnblogs.com/englyf/archive/2023/05/18/17410793.html
-Advertisement-
Play Games

學過 Java、C# 或者其他托管語言(managed languages)的同學,回過頭來看 C++ 的時候,第一反應就是 C++ 沒有自動垃圾回收器(GC),而不能充分利用的資源被稱為垃圾。 ...


*以下內容為本人的學習筆記,如需要轉載,請聲明原文鏈接 微信公眾號「ENG八戒」https://mp.weixin.qq.com/s/7A9-tGZxf4w_7eZl3OUQ4A

學過 Java、C# 或者其他托管語言(managed languages)的同學,回過頭來看 C++ 的時候,第一反應就是 C++ 沒有自動垃圾回收器(GC),而不能充分利用的資源被稱為垃圾。

那麼 C++ 真的不能自動回收垃圾嗎?帶著這個疑問我們來看看一般 C++ 程式都是怎樣回收資源的。

記憶體在電腦系統中是有限的資源,通常申請記憶體和釋放記憶體是這樣子的,假設有個被調用的函數 function():

void function()
{
    int *p = new int; // 申請記憶體

    // 資源申請下來了,不玩有個 p 用?
    // do something

    delete p; // 釋放記憶體
}

這段示例代碼在 function() 函數開始的時候申請了一塊記憶體,大小對應於 int 類型,然後在函數結束的時候釋放它。通常來說,這看起來很OK,沒毛病,但是,如果遇到了下麵幾種情況呢?

  • 程式如果中途有邏輯讓它提前退出 function() 函數
  • 發生了異常而沒有被捕獲到

那麼在函數尾部執行釋放記憶體的動作有幾率不會被執行,意味著發生也會記憶體泄漏。像上面這段代碼,如果調用的次數不多也不礙事,不過,如果迴圈調用 function(),這時泄露的記憶體資源會不斷累積,而且一直被浪費掉,期間系統無法再次使用這些被浪費的記憶體,直到進程被終止,嚴重的話,會導致系統資源被耗盡,跑著跑著系統都崩潰了。這種 bug 在 C 範式的編程語言中真的很常見。

RAII 是什麼

眾所周知 C++ 具有面向對象的特性,在初始化類對象的時候,系統會調用類構造函數。如果類對象是存放在棧空間的話,比如聲明為局部變數,那麼當類對象超出生命周期時,比如退出局部變數的作用域,系統會調用這個對象的類析構函數;如果類對象是存放在堆空間的話,比如通過 new 操作符創建的類對象,那麼當類對象被銷毀時,比如對對象執行 delete 操作,系統同樣會調用類析構函數。

C++ 的這個特性可以用來解決上面提到的資源泄露問題,怎麼利用呢?

modern C++ 實踐建議優先把資源存放在棧上。如果只是個變數類型,完全可以用局部變數的形式定義聲明,這樣代碼塊在退出後系統自動回收棧上的資源。

對上面的函數 function() 修改

void function()
{
    // 聲明定義為局部變數,資源存儲在棧區
    int data = 0;

    // do something with data

    // 函數退出時,自動釋放 data 占用的空間
}

當資源比較占空間時,需要在堆上分配資源,可以通過指針引用它,資源的申請放在類的構造函數里,然後在析構函數里釋放。下麵舉個例子

class Helper
{
private:
    int* data;
public:
    Helper() {
        data = new int; // 在堆上申請記憶體
    }
    ~Helper() {
        delete data; // 釋放堆上申請的記憶體
    }
    void do_something_with_data() {}
};

void function()
{
    // 聲明定義為局部變數,對象存儲在棧區
    // 調用 Helper 類構造函數在堆上申請資源
    Helper help;

    // 通過對象 help 調用成員 data
    // 如果 data 是 Helper 私有成員
    // 在類外面必須通過類成員方法調用 data
    help.do_something_with_data();

    // 函數退出時,自動釋放 help 對象占用的棧空間
    // 就算發生了異常或者中途退出都會執行這一步
    // help 對象被銷毀時,調用 Helper 類析構函數
    // Helper 類析構函數釋放已申請的堆上資源
}

利用這種特性的行為被 C++ 發明人稱呼為 RAII,英文全稱是「resource acquisition is initialization」,中文翻譯過來是「資源獲取即是初始化」。而我喜歡把它叫做上下文管理,實現資源申請釋放的類叫做上下文管理器(context manager)。

經典實踐--智能指針

上面的示例代碼寫起來略顯啰嗦,為了推廣這種設計核心思路和簡化代碼編寫,在 C++ 11 之後標準庫里添加了 unique_ptr。

unique_ptr 屬於 Smart Points 中的一種,Smart Points 在國內通常翻譯為「智能指針」。智能指針負責管理和釋放資源。上面的 function() 函數可以改成這樣子

#include <memory>
void function()
{
    // 實例化智能指針對象,輸入需要被管理的記憶體首地址
    // 對象為局部變數,存儲在棧區
    std::unique_ptr<int> data(new int);

    // 智能指針對象就像普通指針一樣調用
    printf("data=%d\n", *data);

    // 函數退出時,自動釋放 data 對象占用的棧空間
    // 就算發生了異常或者中途退出都會執行這一步
    // data 對象被銷毀時,同步釋放被管理的記憶體資源
}

可見,用了智能指針後,不需要像之前那樣定義類 Helper (上下文管理器)了,代碼清爽很多。

不過,上面的示例代碼中有個地方需要註意,在實例化智能指針對象時必須傳入記憶體地址,有沒有其它更好的方式設置被管理的記憶體地址?

有的,C++ 14 之後標準庫添加了 make_unique,演示一下怎麼用

std::unique_ptr<int> data = std::make_unique<int>();

薦書活動

編程的設計思想是一門很有意思的事情,其中有一門前人總結得很到位的學問叫「設計模式」,想深入瞭解嗎?

最近在聯合機械工業出版社搞薦書活動,這次參與活動的圖書是《深入理解設計模式》,作者是林祥纖。

其中有幾本樣書,八戒 想送給讀者朋友,需要免費領取圖書的朋友可以點擊文章抬頭的原文鏈接!

圖書簡介:

本書以作者與虛擬女友(小璐)在生活中遇到的各種問題作為主線,引出設計模式的各種功能、用途,以及解決方法,系統介紹了23種設計模式,根據具體的實例形象化、具體化地進行了代碼的編寫和詳細講解,讓那些本來對設計模式不太瞭解、一知半解、只有概念的讀者,徹底瞭解和掌握常用的設計模式使用場景及使用方式,並掌握每個設計模式的UML結構和描繪方式。

本書共23章,包括認識設計模式、單例模式、工廠模式、建造者模式、原型模式、適配器模式、裝飾器模式、外觀模式、橋接模式、組合模式、享元模式、代理模式、策略模式、命令模式、狀態模式、模板方法模式、備忘錄模式、中介者模式、觀察者模式、迭代器模式、責任鏈模式、訪問者模式、解釋器模式。

通過以上的知識,讓你從模式小白直接升級為模式大神!本書所需源代碼,均可通過本書配套下載鏈接獲得。 本書適合編程初學者或希望在面向對象編程上有所提高的開發人員閱讀。



您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • GreatSQL社區原創內容未經授權不得隨意使用,轉載請聯繫小編並註明來源。 GreatSQL是MySQL的國產分支版本,使用上與MySQL一致。 作者: 亮 文章來源:GreatSQL社區原創 概念介紹 首先需要知道MySQL中觸發器特點,以及表table相關觸發器載入方式 MySQL中單個tri ...
  • 簡單講,handler就是兩個功能 插入消息,enqueuemessage,msg,when 從消息隊列中遍歷所有消息,比對msg.when和當前的when,找到合適的位置插入 處理消息,looper.loop會從messagequeue中調用next。取消息,如果消息還沒到時間該執行,就會比對時間 ...
  • 大家好,我是 DOM哥。給大家隆重介紹一個文生圖工具
  • 這裡給大家分享我在網上總結出來的一些知識,希望對大家有所幫助 如何優雅的基於 element-plus,封裝一個夢中情 dialog 優點 擺脫繁瑣的 visible 的命名,以及反覆的重覆 dom。 想法 將 dialog 封裝成一個函數就能喚起的組件。如下: addDialog({ title: ...
  • 在前端開發中,模塊化是一種將代碼拆分為獨立模塊的開發方法。它通過將功能相似或相關的代碼組織成可復用、可維護的模塊,以提高開發效率和代碼質量。 模塊化的主要目的是解決傳統的JS開發存在的問題,例如全局命名衝突、代碼復用困難、依賴管理混亂等。通過模塊化,可以將代碼拆分為獨立的功能模塊,每個模塊都有自己的 ...
  • (如何搭建一個vue項目) 一、nvm 安裝與使用 1.1、nvm簡介 nvm全名node.js version management,顧名思義是一個nodejs的版本管理工具。通過它可以安裝和切換不同版本的nodejs 1.2、nvm下載 ①github下載 https://github.com/ ...
  • 策略模式 一、介紹 在策略模式(Strategy Pattern)中,一個類的行為或其演算法可以在運行時更改。這種類型的設計模式屬於行為型模式。 意圖:定義一系列的演算法,把它們一個個封裝起來, 並且使它們可相互替換。 主要解決:在有多種演算法相似的情況下,使用 if...else 所帶來的複雜和難以維護 ...
  • java設計模式【工廠方法模式】 工廠方法模式 工廠方法模式(FACTORY METHOD)是一種常用的類創建型設計模式,此模式的核心精神是封裝類中變化的部分,提取其中個性化善變的部分為獨立類,通過依賴註入以達到解耦、復用和方便後期維護拓展的目的。它的核心結構有四個角色,分別是抽象工廠;具體工廠;抽 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...