C++ 内存管理

2025年6月29日 | 阅读 7 分钟

在 C++ 中,内存管理是管理计算机内存并为程序分配内存空间以提高整体系统性能的过程。内存管理还有用,因为它能够实现 运行时动态内存分配。在此过程中,程序员负责确保内存得到有效利用并被正确释放。如果内存处理不当,可能会导致内存泄漏、段错误、悬空指针和未定义行为。

为什么需要内存管理?

众所周知,数组存储同质数据,因此大多数情况下,内存会在声明时分配给数组。有时,会出现内存的确切大小直到运行时才能确定的情况。如果我们想避免这种情况,我们会声明一个最大尺寸的数组,但有些内存将未被使用。如果我们想避免内存浪费,我们使用 new 运算符在运行时动态分配内存。

内存类型

C++ 中主要有两种内存类型。它们是:

1) 栈内存

栈内存是一种内存类型,在 C++ 程序编译时,操作系统会在此分配空间。它用于存储函数内部声明的所有变量以及与函数相关的其他语句。

语法

当调用函数并返回时,栈内存会自动分配和释放。

C++ 栈内存分配示例

让我们通过一个例子来说明 C++ 中的栈内存分配。

示例

编译并运行

输出

Stack Variable x: 10

说明

在此示例中,`x` 在 `stackFunction()` 函数内部分配在栈上。之后,当函数退出时,`x` 的内存会自动释放。无需显式内存管理(new 或 delete)。

2) 堆内存

在 C++ 中,堆内存也称为动态内存。它是可扩展和可收缩内存的一部分。它由程序员手动控制。其速度比栈内存慢。如果我们使用堆内存,我们可以通过 new 和 delete 运算符在程序执行期间分配和释放内存。我们可以在运行时利用堆内存,这使得它在 C++ 程序中非常有效。

C++ 堆内存示例

让我们通过一个例子来说明 C++ 中的堆内存。

示例

编译并运行

输出

Value stored in heap memory: 25

说明

在此示例中,我们使用 `new int` 分配了堆内存,而不是栈内存。之后,我们创建了一个 `ptr` 指针(在栈上),它指向堆上的一个 `int`。

内存管理运算符

在 C++ 语言中,我们使用 `malloc()` 或 `calloc()` 函数在运行时动态分配内存,并使用 `free()` 函数释放动态分配的内存。C++ 也支持这些函数,但 C++ 还定义了一元运算符,如 `new` 和 `delete`,用于执行相同的任务,即分配和释放内存。

New 运算符

`new` 运算符用于创建对象。另一方面,`delete` 运算符用于删除对象。当使用 `new` 运算符创建对象时,该对象将一直存在,直到我们显式使用 `delete` 运算符删除该对象。因此,我们可以说对象的生命周期与程序的块结构无关。

语法

它具有以下语法:

上面的语法用于使用 `new` 运算符创建对象。在上述语法中,

  • pointer_variable: 它代表指针变量的名称。
  • new: 它代表运算符。
  • data-type: 它定义了数据的类型。

例如

在此语法中,我们使用 `new` 运算符为一个整数分配内存,并将其地址存储在整数指针 `p` 中。我们还可以定义已分配内存的初始值。

C++ New 运算符示例

让我们通过一个示例来说明 C++ 中的 new 操作符。

示例

编译并运行

输出

Number: 25

说明

在此示例中,我们使用 `new int` 在堆上为单个整数分配了内存。之后,`*num = 25` 将值 25 存储在分配的内存中。最后,`delete num` 运算符释放内存以避免内存泄漏。

如何在 C++ 中创建一维数组?

我们知道,`new` 运算符用于为任何数据类型甚至用户定义的数据类型(如数组、结构体、联合体等)创建内存空间,因此创建一维数组的语法如下所示

例如

在此示例中,我们创建了一个类型为 `int` 的数组,其大小为 8,其中 `p[0]` 指的是第一个元素,`p[1]` 指的是第一个元素,依此类推。

Delete 运算符

在 C++ 中,`delete` 运算符主要用于释放动态内存分配。当不再需要内存时,需要将其释放,以便内存可以用于其他目的。这可以通过使用 `delete` 运算符来实现。

语法

它具有以下语法:

也可以使用以下语法从内存空间中移除动态分配的数组

在此语句中,我们需要指定定义需要释放的元素数量的大小。此语法的缺点是我们必须记住数组的大小。

C++ Delete 运算符示例

让我们通过一个示例来说明 C++ 中的 delete 操作符。

示例

编译并运行

输出

Enter the size of the array: 5

Enter the element: 1
2
3
4
5

The elements that you have entered are: 1,2,3,4,5,

说明

在此示例中,我们使用 `new` 运算符创建了一个数组。之后,程序将在运行时获取用户输入的数组大小。当程序完成所有操作后,它使用语句 `delete arr` 删除该对象。

New 运算符的优点

与 `malloc()` 函数相比,`new` 运算符有几个优点

  • 它不使用 `sizeof()` 运算符,因为它会自动计算数据对象的大小。
  • 它会自动返回正确的数据类型指针,因此无需使用类型转换。
  • 与其他运算符一样,`new` 和 `delete` 运算符也可以被重载。
  • 它还允许我们在为对象创建内存空间时初始化数据对象。

Delete 运算符的优点

C++ 中 `delete` 运算符的几个优点如下

  • 如果不使用 `delete` 运算符,动态分配的内存即使在不再需要时也会被保留。
  • 它为我们提供了完全的控制权,可以控制何时以及如何释放 C++ 中的内存。
  • 当对指向对象的指针 调用 `delete` 运算符时,对象的 析构函数也会被自动调用。
  • 这使得我们的程序在内存效率方面更高,更适合大规模或资源受限的系统。

C++ 内存管理选择题

1) 当声明一个变量的函数退出时,该变量在栈上的会发生什么?

  1. 它应该被手动删除
  2. 它会被自动释放
  3. 它会变成一个全局变量
  4. 它会导致内存泄漏
 

答案:b) 它会被自动释放


2) 以下哪个语法正确地在堆上分配和释放数组?

  1. int* arr = new int(5); delete arr;
  2. int* arr = new int[5]; delete[] arr;
  3. int arr[5]; delete arr;
  4. int* arr = malloc(5 * sizeof(int)); free(arr);
 

答案:b) int* arr = new int[5]; delete[] arr;


3) 当只有一个所有者应该管理动态分配对象的生命周期时,应该使用哪个智能指针?

  1. std::shared_ptr
  2. std::weak_ptr
  3. std::unique_ptr
  4. std::auto_ptr
 

答案:c) std::unique_ptr


4) 对一个指针调用两次 delete(重复 delete)会发生什么?

  1. 它会安全地释放内存
  2. 它会导致编译错误
  3. 它会导致未定义行为
  4. 它会重新初始化内存
 

答案:c) 它会导致未定义行为


5) `std::weak_ptr` 在智能指针管理中的目的是什么?

  1. 创建非拥有引用并避免循环依赖
  2. 增加引用计数
  3. 管理独占所有权
  4. 在堆上分配内存
 

答案:a) 创建非拥有引用并避免循环依赖