C++内存管理详解
目录
1.C/C++中的内存
2.C++内存管理
2.1C语言内存管理
2.2new和delete
2.2.1概念及定义
2.2.2自定义类型内存管理
2.2.3 delete与delete[ ]
1.C/C++中的内存
在C/C++中编译器会对不同的代码进行内存分配,给代码的内存区主要分为栈区、堆区、数据段(静态区)、代码段(常量区)。像常见的局部变量就分配在栈区,具体内存分区如图所示:
说明:
(1)栈又叫堆栈,主要储存非静态局部变量、函数参数、返回值等,且向下增长
(2)堆主要用于程序运行时动态分配内存,是向上增长的
(3)数据段主要储存全局数据(变量),静态数据(变量)
(4)代码段主要储存可执行代码、只读数据(数据)。
对于栈和堆的向下、向上调整:C/C++中的内存分区从代码段到栈是从低地址到高地址,向下增长的意思是使用栈时优先使用高地址内存,由高到低;向上增长的意思是有限使用低内存地址,由低到高。以代码为例:
#include<iostream>using namespace std;int main()
{int x = 0;int y = 0;int* a = (int*)malloc(sizeof(int));int* b = (int*)malloc(sizeof(int));cout << "x:" << &x << endl;cout << "y:" << &y << endl;cout << endl;cout << "a:" << a << endl;cout << "b:" << b << endl;return 0;
}
运行结果如下(VS2022 x86 Debug环境):
可以看到在栈开辟的局部变量x,y:x比y先定义,x的地址也比y的地址大;对于在堆区开辟的a,b:a比b先开辟,a地址也比b小。但堆区不一定全是向上增长,为提高堆的利用效率,当开辟空间较小时可能会在之前的未使用的空间进行开辟。
2.C++内存管理
2.1C语言内存管理
C语言中利用malloc/calloc/realloc动态管理内存:
(1)malloc快速分配指定大小的内存,不进行初始化
void* malloc (size_t size);
(2)calloc既分配内存又进行初始化
void* calloc (size_t num, size_t size);
(3)realloc对指定位置内存进行再分配
void* realloc (void* ptr, size_t size);
2.2new和delete
2.2.1概念及定义
new和delete是C++中用于动态管理内存的运算符,new用于给对象开辟空间,delete用于销毁对象空间,具体语法定义如下(以内置类型为例):
#include<iostream>using namespace std;void test1()
{//管理对象int* ptr4 = new int;int* ptr5 = new int(3);delete ptr4;delete ptr5;//管理对象数组int* ptr6 = new int[3];delete[] ptr6;
}int main()
{test1;return 0;
}
具体分析如下:
2.2.2自定义类型内存管理
既然new和delete与C中动态内存管理差不多,那为什么还要设计这两个运算符呢?C++中引入了自定义类型,为自定义类型开辟空间也算实例化的一种,但使用malloc等函数无法在开辟内存时调用构造初始化,所以在C++中引入new和delete来动态管理自定义类型空间。
new和delete更抽象的来说更像一种运算符重载,new将开辟空间与调用构造封装在一起,而delete则将空间销毁与析构封装在一起。下面以Stack为例进一步理解:
#include<iostream>using namespace std;template<class T>
class Stack
{
public:Stack(int n = 4):_arr(new T[n]),_size(0),_capacity(0){cout << "Stack(int)" << endl;}~Stack(){if (_size != 0){free(_arr);_arr = nullptr;_size = _capacity = 0;cout << "~Stack()" << endl;}}void Push(T x){_arr[_size] = x;++_size;}private:T* _arr;int _size;int _capacity;
};void test2()
{Stack<int>* s1 = new Stack<int>;(*s1).Push(1);(*s1).Push(2);(*s1).Push(3);delete s1;
}int main()
{test2();return 0;
}
下面利用图解理解:
遇见对象中再开空间的情况:new会优先给对象开空间,然后调用构造函数给对象成员初始化;delete销毁时会优先调用析构函数销毁对象中的资源,随后再销毁对象。
2.2.3 delete与delete[ ]
在C++中new与new[ ],delete与delete[ ]有着不同重载。其中一方面是new[ ]与delete[ ]要开辟多次空间以及调用多次对象构造与析构,实现方法不同;另一方面new[ ]会在开辟所需内存外,另外留出部分空间来记录所开辟的大小参数,方便delete[ ]来正确销毁空间大小,相应的delete[ ]会从记录空间销毁,而delete则是直接从指针起始位置开始销毁。
所以这里new与delete,new[ ]与delete[ ]要匹配使用。