C++ 动态内存分配
在编写C++程序时,有效管理内存是提高程序性能和稳定性的关键因素。C++提供了强大的动态内存分配功能,允许程序在运行时按需分配和释放内存。本文将全面介绍C++动态内存分配的基本概念、操作方法以及最佳实践。
什么是动态内存分配?
在C++中,内存分配主要分为两种类型:
- 静态内存分配:在编译时确定大小,程序执行期间内存大小固定
- 动态内存分配:在运行时根据需 要分配内存,使用完毕后可以释放
动态内存分配允许程序根据实际需求分配内存,这在处理大量数据或事先不知道所需内存大小的情况下特别有用。
备注
C++中的内存主要分为栈内存和堆内存。静态分配的变量通常存储在栈上,而动态分配的内存则位于堆上。
C++ 中动态内存的基本操作
使用new和delete运算符
C++提供了new
和delete
运算符来进行动态内存的分配和释放:
// 分配单个对象的内存
int* pInt = new int;
*pInt = 5;
std::cout << "动态分配的整数: " << *pInt << std::endl;
delete pInt; // 释放内存
// 分配带初始值的对象
double* pDouble = new double(3.14);
std::cout << "动态分配的浮点数: " << *pDouble << std::endl;
delete pDouble;
输出:
动态分配的整数: 5
动态分配的浮点数: 3.14
分配和释放数组
使用new[]
和delete[]
来分配和释放数组:
// 分配整数数组
int* pArray = new int[5];
for (int i = 0; i < 5; i++) {
pArray[i] = i * 10;
}
std::cout << "动态数组内容: ";
for (int i = 0; i < 5; i++) {
std::cout << pArray[i] << " ";
}
std::cout << std::endl;
delete[] pArray; // 使用delete[]释放数组
输出:
动态数组内容: 0 10 20 30 40
警告
使用new[]
分配的内存必须使用delete[]
释放,否则可能导致内存泄漏或未定义行为。
内存分配失败处理
当系统内存不足时,new
操作可能会失败。在C++中,有两种处理方式:
1. 异常处理
默认情况下,当new
操作失败时会抛出std::bad_alloc
异常:
try {
int* hugeArray = new int[10000000000]; // 尝试分配大量内存
// 使用内存...
delete[] hugeArray;
} catch (const std::bad_alloc& e) {
std::cerr << "内存分配失败: " << e.what() << std::endl;
}
2. nothrow形式
使用nothrow
参数可以让new
在失败时返回空指针而不是抛出异常:
int* ptr = new(std::nothrow) int[1000000000];
if (ptr == nullptr) {
std::cout << "内存分配失败,返回nullptr" << std::endl;
} else {
// 使用内存...
delete[] ptr;
}
常见问题与注意事项
1. 内存泄漏
内存泄漏是指分配的内存没有被正确释放,导致程序长时间运行时可用内存逐渐减少:
void memoryLeakExample() {
int* ptr = new int(42);
// 没有调用delete,导致内存泄漏
}
2. 悬挂指针
悬挂指针是指指针指向的内存已被释放,但指针本身没有置为nullptr:
int* ptr = new int(42);
delete ptr;
// ptr现在是悬挂指针
// *ptr = 10; // 危险操作,可能导致程序崩溃
ptr = nullptr; // 正确做法:释放后将指针置为nullptr
3. 双重释放
对同一内存区域执行多次delete操作会导致未定义行为:
int* ptr = new int(42);
delete ptr;
// delete ptr; // 错误:双重释放
智能指针
为了避免手动管理内存带来的问题,C++11引入了智能指针。智能指针是对原始指针的封装,能够自动管理内存的生命周期。
1. unique_ptr
std::unique_ptr
实现了独占所有权的概念,确保同一时间只有一个指针可以指向该内存:
#include <iostream>
#include <memory>
void useUniquePtr() {
std::unique_ptr<int> ptr1(new int(42));
// 更现代的方式
auto ptr2 = std::make_unique<int>(100); // C++14及以上
std::cout << "ptr1值: " << *ptr1 << std::endl;
std::cout << "ptr2值: " << *ptr2 << std::endl;
// 转移所有权
std::unique_ptr<int> ptr3 = std::move(ptr1);
// 此时ptr1为nullptr
if (!ptr1) {
std::cout << "ptr1现在为nullptr" << std::endl;
}
// 离开作用域时,ptr2和ptr3会自动释放内存
}
输出:
ptr1值: 42
ptr2值: 100
ptr1现在为nullptr
2. shared_ptr
std::shared_ptr
实现了共享所有权,多个指针可以指向同一块内存,当最后一个指针被销毁时,内存会被释放:
#include <iostream>
#include <memory>
void useSharedPtr() {
auto ptr1 = std::make_shared<int>(42);
std::cout << "引用计数: " << ptr1.use_count() << std::endl;
{
auto ptr2 = ptr1; // 共享所有权
std::cout << "引用计数: " << ptr1.use_count() << std::endl;
} // ptr2离开作用域
std::cout << "引用计数: " << ptr1.use_count() << std::endl;
// ptr1离开作用域时,内存会被释放
}
输出:
引用计数: 1
引用计数: 2
引用计数: 1