避免 C++ 中的内存泄漏

Jinku Hu 2023年1月30日 2021年4月29日
  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;
}
Author: Jinku Hu
Jinku Hu avatar Jinku Hu avatar

Founder of DelftStack.com. Jinku has worked in the robotics and automotive industries for over 8 years. He sharpened his coding skills when he needed to do the automatic testing, data collection from remote servers and report creation from the endurance test. He is from an electrical/electronics engineering background but has expanded his interest to embedded electronics, embedded programming and front-/back-end programming.

LinkedIn

相关文章 - C++ Memory