避免 C++ 中的記憶體洩漏

Jinku Hu 2023年10月12日
  1. 使用 delete 操作符釋放每個 new 分配的記憶體塊
  2. 使用 std::unique_ptr 自動釋放堆物件
避免 C++ 中的記憶體洩漏

本文將演示有關如何避免 C++ 中的記憶體洩漏的多種方法。

使用 delete 操作符釋放每個 new 分配的記憶體塊

記憶體洩漏是直接與原始動態記憶體介面進行互動的程式的常見問題,這給程式設計師增加了負擔,以相應的方法釋放每個分配的記憶體。在 C++ 中可以訪問多個用於記憶體管理的介面,以及一些特定於平臺的系統呼叫,這些介面實質上代表了可用於此目的的最低階別的功能。在大多數情況下,常規程式應僅使用特定於語言的功能(例如 newdelete 操作符)進行手動記憶體管理,或使用智慧指標進行自動記憶體重新分配。

記憶體洩漏最易受攻擊的方法是使用 new/delete 操作符直接管理堆記憶體。請注意,從 new 呼叫返回的每個指標都應傳遞給 delete 操作符,以將相應的記憶體塊釋放回系統,否則可能會耗盡記憶體。例如,下面的程式分配一個 10 個元素的 char 陣列,並且在程式退出前不釋放它。因此,該程式據說會發生記憶體洩漏。

#include <iostream>

using std::cout;
using std::endl;

constexpr int SIZE = 10;

int main() {
  char *arr = new char[SIZE];

  for (int i = 0; i < SIZE; ++i) {
    arr[i] = (char)(65 + i);
    cout << arr[i] << "; ";
  }
  cout << endl;

  return EXIT_SUCCESS;
}

可以使用 valgrind 程式檢測記憶體洩漏,該程式是一個命令列實用程式,可以在已編譯的程式檔案上執行。請注意,Valgrind 實際上是一組多個工具,其中一個恰好是記憶體檢查實用程式。可以發出以下命令來調查記憶體洩漏。

valgrind --tool=memcheck --leak-check=full ./program

完成檢查後,將輸出以下輸出。注意,由於我們沒有在 arr 指標上呼叫 delete,所以整個陣列導致未釋放的記憶體,因此在洩漏摘要中記錄了 - definitely lost: 10 bytes。後者是 10 個元素的字元陣列的實際大小。

==13732== HEAP SUMMARY:
==13732==     in use at exit: 10 bytes in 1 blocks
==13732==   total heap usage: 3 allocs, 2 frees, 73,738 bytes allocated
==13732==
==13732== 10 bytes in 1 blocks are definitely lost in loss record 1 of 1
==13732==    at 0x483C583: operator new[](unsigned long) (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
==13732==    by 0x1091FE: main (tmp.cpp:11)
==13732==
==13732== LEAK SUMMARY:
==13732==    definitely lost: 10 bytes in 1 blocks
==13732==    indirectly lost: 0 bytes in 0 blocks
==13732==      possibly lost: 0 bytes in 0 blocks
==13732==    still reachable: 0 bytes in 0 blocks
==13732==         suppressed: 0 bytes in 0 blocks
==13732==
==13732== For lists of detected and suppressed errors, rerun with: -s
==13732== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)

使用 std::unique_ptr 自動釋放堆物件

前面的例子程式碼可以通過在程式退出前的 delete [] arr; 語句來解決。雖然,另一種選擇是利用智慧指標之一,例如,std::unique_ptr。可以使用給定的型別初始化智慧指標物件,並儲存從 new 操作符返回的指標。與常規指標的唯一區別是與 std::unique_ptr 相關的儲存區域不需要顯式地 delete-d。相反,一旦物件超出範圍,編譯器將負責釋放。

#include <iostream>
#include <memory>

using std::cout;
using std::endl;

constexpr int SIZE = 10;

int main() {
  std::unique_ptr<char[]> arr(new char[SIZE]);

  for (int i = 0; i < SIZE; ++i) {
    arr[i] = (char)(65 + i);
    cout << arr[i] << "; ";
  }
  cout << endl;

  return EXIT_SUCCESS;
}
作者: Jinku Hu
Jinku Hu avatar Jinku Hu avatar

DelftStack.com 創辦人。Jinku 在機器人和汽車行業工作了8多年。他在自動測試、遠端測試及從耐久性測試中創建報告時磨練了自己的程式設計技能。他擁有電氣/ 電子工程背景,但他也擴展了自己的興趣到嵌入式電子、嵌入式程式設計以及前端和後端程式設計。

LinkedIn Facebook

相關文章 - C++ Memory