C++ 动态内存管理

在 C++ 中,动态内存管理允许在程序运行时分配和释放内存空间。与静态内存分配不同,动态内存分配的内存大小可以根据程序的需要来决定,这为编程提供了更大的灵活性。C++ 提供了两种主要的操作来管理动态内存:new
和 delete
。
1. 动态内存分配
1.1 使用 new
操作符
new
操作符用于在堆(heap)上分配内存,并返回指向该内存的指针。与在栈上分配内存不同,堆上的内存可以在整个程序生命周期中访问,直到手动释放。
分配单个对象的内存
#include <iostream>
using namespace std;
int main() {
int* p = new int; // 分配一个 int 类型的内存空间
*p = 5; // 为这个内存位置赋值
cout << "Value: " << *p << endl; // 输出:Value: 5
delete p; // 释放内存
return 0;
}
说明:
new int
:在堆上分配一个int
类型的内存空间。delete p
:释放之前通过new
分配的内存。
分配数组的内存
#include <iostream>
using namespace std;
int main() {
int* arr = new int[5]; // 分配一个包含 5 个整数的数组
// 给数组赋值
for (int i = 0; i < 5; ++i) {
arr[i] = i * 10;
}
// 输出数组元素
for (int i = 0; i < 5; ++i) {
cout << arr[i] << " ";
}
cout << endl;
delete[] arr; // 释放数组的内存
return 0;
}
说明:
new int[5]
:分配一个包含 5 个整数的动态数组。delete[] arr
:释放通过new[]
分配的数组内存。
1.2 使用 new
返回的指针
当你使用 new
分配内存时,它返回一个指向已分配内存的指针。如果内存分配失败(例如,内存不足),new
会抛出 std::bad_alloc
异常。
#include <iostream>
#include <new> // 包含 std::bad_alloc
using namespace std;
int main() {
try {
int* p = new int[10000000000]; // 尝试分配一个巨大的数组
}
catch (const bad_alloc& e) {
cout << "Memory allocation failed: " << e.what() << endl;
}
return 0;
}
说明:
std::bad_alloc
:如果内存分配失败,new
会抛出此异常。
2. 动态内存释放
在 C++ 中,使用 new
分配的内存必须手动释放,否则会发生内存泄漏(memory leak)。释放内存使用 delete
(单个对象)或 delete[]
(数组)操作符。
2.1 释放单个对象的内存
#include <iostream>
using namespace std;
int main() {
int* p = new int(10); // 分配并初始化内存
cout << "Value: " << *p << endl; // 输出:Value: 10
delete p; // 释放内存
return 0;
}
2.2 释放数组的内存
#include <iostream>
using namespace std;
int main() {
int* arr = new int[3] {1, 2, 3}; // 分配并初始化数组
for (int i = 0; i < 3; ++i) {
cout << arr[i] << " "; // 输出:1 2 3
}
cout << endl;
delete[] arr; // 释放数组内存
return 0;
}
注意:
- 使用
delete
时要注意区分单个对象和数组对象。对于数组对象,必须使用delete[]
来释放内存。
3. C++11 智能指针
C++11 引入了智能指针(std::unique_ptr
和 std::shared_ptr
),这些指针可以自动管理动态分配的内存,从而避免手动管理内存的麻烦。
3.1 std::unique_ptr
std::unique_ptr
是一种智能指针,它确保只有一个指针可以管理某个内存。离开作用域时,std::unique_ptr
会自动释放内存。
#include <iostream>
#include <memory>
using namespace std;
int main() {
unique_ptr<int> ptr(new int(10)); // 分配内存并初始化
cout << "Value: " << *ptr << endl; // 输出:Value: 10
// 不需要显式调用 delete,ptr 超出作用域时自动释放内存
return 0;
}
3.2 std::shared_ptr
std::shared_ptr
是一个智能指针,它允许多个指针共享对同一块内存的所有权。内存会在最后一个指针离开作用域时自动释放。
#include <iostream>
#include <memory>
using namespace std;
int main() {
shared_ptr<int> ptr1 = make_shared<int>(20); // 创建并初始化共享指针
{
shared_ptr<int> ptr2 = ptr1; // ptr1 和 ptr2 共享同一块内存
cout << "Shared value: " << *ptr2 << endl; // 输出:Shared value: 20
} // ptr2 超出作用域,内存不会立即释放,因为 ptr1 仍然拥有所有权
// 当 ptr1 超出作用域时,内存才会被释放
return 0;
}
3.3 智能指针优势
- 自动内存管理:智能指针能够自动释放内存,避免内存泄漏。
- 内存共享:
std::shared_ptr
允许多个对象共享对同一块内存的所有权。
4. 内存泄漏和防止内存泄漏
内存泄漏是指程序分配的内存未能及时释放,导致无法再访问和使用该内存。这会导致程序的内存占用不断增加,最终可能导致程序崩溃。
4.1 内存泄漏的示例
#include <iostream>
using namespace std;
int main() {
int* p = new int(100); // 动态分配内存
// 忘记释放内存,导致内存泄漏
return 0;
}
解决方案:
- 始终确保在使用完动态内存后使用
delete
或delete[]
释放内存。 - 可以考虑使用智能指针(
std::unique_ptr
和std::shared_ptr
)来管理内存,减少内存泄漏的风险。
5. 总结
- 动态内存分配:使用
new
操作符来分配内存。 - 动态内存释放:使用
delete
(单个对象)或delete[]
(数组)释放内存。 - 智能指针:
std::unique_ptr
和std::shared_ptr
自动管理动态内存,避免内存泄漏。 - 内存泄漏:如果动态内存没有释放,就会发生内存泄漏,使用智能指针可以有效防止这种情况。
通过理解并使用 C++ 的动态内存管理功能,你可以更加高效地控制内存的使用和释放,从而提高程序的性能和稳定性。