您现在的位置是:首页 >技术杂谈 >C/C++动态内存管理 ,new和delete网站首页技术杂谈
C/C++动态内存管理 ,new和delete
目录
C语言动态内存管理<-点这里
C++动态内存管理
因为C语言中的管理方式使用起来不方便而且有些情况下无法解决问题,C++通过new、delete关键字进行动态内存管理,delete和new必须配套使用,不能new开辟空间使用free释放空间。
内置类型的动态内存管理
//动态申请1个int类型的空间
int* p1 = new int;
delete p1;
//动态申请1个int类型的空间并初始化为10
int* p2 = new int(10);
delete p2;
//动态申请10个int类型的空间
int* p3 = new int[10];
delete[] p3;
//动态申请10个int类型的空间并初始化
int* p4 = new int[10]{1,2,3,4,5,6,7,8,9,10};
delete[] p4;
//动态申请一个10行10列的二维数组
//int(*p)[n] 是一个数组指针,该数组有10个int类元素
int(*arr1)[10] = new int[10][10];
delete[] arr1;
//用二级指针模拟实现10行10列的二维数组
//内存开辟
int** arr2 = new int*[10];
for (int i = 0;i < 10;i++)
{
arr2[i] = new int[10];
}
//空间释放
for (int i = 0;i < 10;i++)
{
delete[] arr2[i];
}
delete[] arr2;
申请和释放单个元素的空间使用new和delete,申请和释放连续的空间使用new[]和delete[]
自定义类型的动态内存管理
new为自定义类型分配空间后,会调用构造函数,delete释放自定义类型空间前会调用析构函数
class Stack
{
private:
int* _arr;
int _capacity;
int _top;
public:
Stack()
:_capacity(4)
,_top(0)
{
_arr = new int[4];
}
Stack(int capacity)
:_capacity(capacity)
,_top(0)
{
_arr = new int[capacity];
}
~Stack()
{
delete[] _arr;
}
};
int main()
{
Stack* st1 = new Stack(10);
delete st1;
Stack* st2 = new Stack[3];
delete[] st2;
Stack* st3 = new Stack[3]{ 5,2,4 };
delete[] st3;
Stack* st4 = new Stack[3]{ Stack(5),Stack(2),Stack(4) };
delete[] st4;
}
注意:自定义类型数组初始化有多种方法,如果有单参构造函数或者除了缺省参数只有一个参数的构造函数,数组初始化可以直接用该参数类型的值,直接放到{ }内初始化,因为它会自动转换成对象来初始化数组;还可以用匿名对象初始化。
new和delete的实现原理
因为malloc和calloc的功能是开辟空间,如果开辟空间失败返回0。但是对于C++面向对象的语言来说,往往遇到问题要抛异常,所以会用operator new来封装malloc,以达到抛异常的功能。operator delete也是如此。
new的原理
1. 调用operator new函数申请空间
2. 在申请的空间上执行构造函数,完成对象的构造
delete的原理
1. 在空间上执行析构函数,完成对象中资源的清理工作
2. 调用operator delete函数释放对象的空间
new T[N]的原理
1. 调用operator new[]函数,在operator new[]中实际调用operator new函数完成N个对
象空间的申请
2. 在申请的空间上执行N次构造函数
delete[]的原理
1. 在释放的对象空间上执行N次析构函数,完成N个对象中资源的清理
2. 调用operator delete[]释放空间,实际在operator delete[]中调用operator delete来释
放空间
new和malloc、delete和free的异同
相同点:new和malloc、delete和free都是从堆上申请空间,都需要对空间手动释放
不同点:
- (特性)new/delete是关键字,malloc/free是函数
- (特性)malloc使用时需要计算空间大小,new会根据数据类型自动计算,如果有多个数据直接在[ ]指定数量即可
- (特性)malloc的返回值是void*,使用时需要强制类型转换,而new不需要。
- (特性)malloc申请空间失败返回NULL,而new需要捕获异常
- (功能)malloc申请的空间不会初始化,new申请的空间可以初始化
- (最重要)(功能)malloc/free只会开辟和释放空间,而new会先调用operator new开辟空间再调用构造函数初始化,delete会先调用析构函数完成空间资源的清理再调用operator delete释放空间。
new的其他用法:定位new
作用:在已经开辟好的一块空间调用构造函数初始化
格式:new(address)custom type(initial list)或者new(address)basic type(initial list)
address是开辟的空间的地址;
custom type表示自定义类型;
initial list表示自定义类型的初始化列表;
basic type表示基本数据类型
使用场景:
定位new表达式在实际中一般是配合内存池使用。因为内存池分配出的内存没有初始化,所以如
果是自定义类型的对象,需要使用new的定义表达式进行显示调构造函数进行初始化。
优势:
内存的申请和释放有一定的开销,遇到需要频繁申请释放内存的情况时,可以一次性开好空间,使用定位new调用构造函数初始化,可以提高效率。
class Date
{
friend ostream& operator<<(ostream& _cout, const Date& d);
private:
int _year;
int _month;
int _day;
public:
Date(int year = 2023, int month = 1, int day = 1)
:_year(year),
_month(month),
_day(day)
{}
~Date()
{}
};
//流插入运算符重载
ostream& operator<<(ostream& _cout,const Date& d)
{
_cout << d._year << "-" << d._month << "-" << d._day;
return _cout;
}
int main()
{
//基本数据类型
int i = 5;
int* p1 = (int*)malloc(sizeof(int));
new(p1)int(10);
cout << *p1 << endl;//10
new(p1)int(i);
cout << *p1 << endl;//5
free(p1);
//自定义数据类型
Date* p2 = (Date*)malloc(sizeof(Date));
new(p2)Date();//无参时,初始化的()可加可不加
cout << *p2 << endl;//2023-1-1
new(p2)Date(2023, 5, 20);
cout << *p2 << endl;//2023-5-20
p2->~Date();
free(p2);
}