文章目录
- 堆和栈
- new和delete操作符
- 数组的动态内存分配
- 对象的动态内存分配
堆和栈
在C++中
- 栈:是一种静态内存分配区域,用于存储局部变量和函数调用的上下文信息。在栈上的内存分配和释放都是自动管理的,遵循后进先出(LIFO)原则。
- 堆:是一种动态内存分配区域,用于存储动态分配的数据,如对象、数据和数据结构等,在堆上的内存分配和释放需要显式管理,例如使用new或malloc操作符来分配内存,使用delete和free等操作符来释放内存。
在很多时候,你无法提前预知需要多少内存来存储某个定义变量中的特定信息,所需内存的大小需要在运行时才能确定。
在 C++ 中,您可以使用特殊的运算符为给定类型的变量在运行时分配堆内的内存,这会返回所分配的空间地址。这种运算符即 new 运算符。
如果您不再需要动态分配的内存空间,可以使用 delete 运算符,删除之前由 new 运算符分配的内存。
new和delete操作符
new
和 delete
运算符是C++中用于动态内存管理的操作符。它们允许你在堆上分配和释放内存,用于存储动态创建的对象和数据结构。
new
运算符:
new
运算符用于在堆上分配内存并构造一个对象。- 语法:
new 数据类型
或new 数据类型[元素个数]
。 - 对于单个对象的分配,
new
返回指向该对象的指针。 - 对于数组的分配,
new
返回指向数组的首元素的指针。 - 你需要手动释放使用
new
分配的内存,否则会导致内存泄漏。
例如,使用 new
创建一个整数对象的示例:
int* dynamicInt = new int; // 分配一个整数的内存
*dynamicInt = 42;
delete
运算符:
delete
运算符用于释放使用new
分配的内存,并调用对象的析构函数(如果适用)。- 语法:
delete 指针
或delete[] 指针
。 - 你需要明确指定要释放的内存的指针,以避免悬挂指针问题。
例如,使用 delete
释放先前使用 new
分配的整数对象内存的示例:
delete dynamicInt; // 释放使用 new 分配的内存
new
和 delete
的数组形式:
你还可以使用 new[]
和 delete[]
运算符来分配和释放动态数组的内存。在分配动态数组时,使用 new[]
,在释放内存时使用 delete[]
。这些运算符对于分配和释放动态数组内存非常有用。
例如,使用 new[]
创建一个包含整数的数组,并使用 delete[]
释放内存的示例:
int* dynamicArray = new int[5]; // 分配一个包含 5 个整数的数组的内存
// 使用 dynamicArray 指向的内存
delete[] dynamicArray; // 释放使用 new[] 分配的数组内存
在C++中,可以通过检查指针是否为nullptr(空指针)来确定是否成功分配了内存。
例如:
int* dynamicInt = new int; // 尝试分配内存
if (dynamicInt != nullptr) {
// 分配成功
*dynamicInt = 42;
} else {
// 分配失败
// 执行错误处理逻辑
}
注:malloc() 函数在 C 语言中就出现了,在 C++ 中仍然存在,但建议尽量不要使用 malloc() 函数。new 与 malloc() 函数相比,其主要的优点是,new 不只是分配了内存,它还创建了对象。
完整的示例:
#include <iostream>
int main(){
// 使用 new 分配内存并创建整数对象
int* dynamicInt = new int;
if (dynamicInt != nullptr){
*dynamicInt = 42;
std::cout << "Dynamic integer value: " << *dynamicInt << std::endl;
}
else{
std::cerr << "Memory allocation failed" << std::endl;
return 1; // 退出程序,表示分配内存失败。
}
// 使用 delete 释放内存并销毁整数对象
delete dynamicInt;
return 0;
}
数组的动态内存分配
上面已经讲了关于new和delete的数组形式。扩展一下多维数组。
一维数组:
// 动态分配,数组长度为 m
int *array=new int [m];
//释放内存
delete [] array;
二维数组:
int **array;
// 假定数组第一维长度为 m, 第二维长度为 n
// 动态分配空间
array = new int *[m];
for( int i=0; i<m; i++ )
{
array[i] = new int [n];
}
//释放
for( int i=0; i<m; i++ )
{
delete [] array[i];
}
delete [] array;
示例:
#include <iostream>
int main() {
int numRows = 3;
int numCols = 4;
// 动态分配二维数组
int** dynamicArray = new int*[numRows]; // 分配行指针数组
for (int i = 0; i < numRows; i++) {
dynamicArray[i] = new int[numCols]; // 分配每行的列数组
}
// 初始化二维数组
int count = 1;
for (int i = 0; i < numRows; i++) {
for (int j = 0; j < numCols; j++) {
dynamicArray[i][j] = count++;
}
}
// 打印二维数组
for (int i = 0; i < numRows; i++) {
for (int j = 0; j < numCols; j++) {
std::cout << dynamicArray[i][j] << ' ';
}
std::cout << std::endl;
}
// 释放动态分配的内存
for (int i = 0; i < numRows; i++) {
delete[] dynamicArray[i]; // 释放每行的列数组
}
delete[] dynamicArray; // 释放行指针数组
return 0;
}
三维数组:
int ***array;
// 假定数组第一维为 m, 第二维为 n, 第三维为h
// 动态分配空间
array = new int **[m];
for( int i=0; i<m; i++ )
{
array[i] = new int *[n];
for( int j=0; j<n; j++ )
{
array[i][j] = new int [h];
}
}
//释放
for( int i=0; i<m; i++ )
{
for( int j=0; j<n; j++ )
{
delete[] array[i][j];
}
delete[] array[i];
}
delete[] array;
示例:
#include <iostream>
int main() {
int x = 3;
int y = 4;
int z = 2;
// 动态分配三维数组
int*** dynamicArray = new int**[x]; // 分配 x 个二维数组
for (int i = 0; i < x; i++) {
dynamicArray[i] = new int*[y]; // 分配每个二维数组的 y 行
for (int j = 0; j < y; j++) {
dynamicArray[i][j] = new int[z]; // 分配每行的 z 列
}
}
// 初始化三维数组
int count = 1;
for (int i = 0; i < x; i++) {
for (int j = 0; j < y; j++) {
for (int k = 0; k < z; k++) {
dynamicArray[i][j][k] = count++;
}
}
}
// 打印三维数组
for (int i = 0; i < x; i++) {
for (int j = 0; j < y; j++) {
for (int k = 0; k < z; k++) {
std::cout << dynamicArray[i][j][k] << ' ';
}
std::cout << std::endl;
}
}
// 释放动态分配的内存
for (int i = 0; i < x; i++) {
for (int j = 0; j < y; j++) {
delete[] dynamicArray[i][j]; // 释放每行的列数组
}
delete[] dynamicArray[i]; // 释放每个二维数组的行指针数组
}
delete[] dynamicArray; // 释放 x 个二维数组的指针数组
return 0;
}
对象的动态内存分配
示例:
#include <iostream>
using namespace std;
class Box
{
public:
Box(){
cout << "调用构造函数!" << endl;
}
~Box(){
cout << "调用析构函数!" << endl;
}
};
int main()
{
Box* myBoxArray = new Box[4];
delete [] myBoxArray; // 删除数组
return 0;
}
如果要为一个包含四个 Box 对象的数组分配内存,构造函数将被调用 4 次,同样地,当删除这些对象时,析构函数也将被调用相同的次数(4次)。
当上面的代码被编译和执行时,它会产生下列结果: