【动态内存2】:C++中的内存开辟与释放&&底层实现
new与delete是用于开辟内存与释放内存,用于堆内存管理的两个关键字(标识符),与C语言中的malloc/free对应,new与delete相较于malloc和free实现机制更为复杂,new申请空间时还会调用构造函数,delete释放空间时还会析构函数。
注意:new/delete ;malloc 与free都要成对使用哦!!!以免内存泄漏
一、new的用法
new是关键字,返回的是一个指向所分配类型变量(对象)的指针。对所创建的变量或对象,都是通过该指针来间接操作的。
(一)开辟单个变量
1、开辟方式
- 指针变量名 = new + 类型标识符 ; int * p = new int // 开辟一个存放整数的存储空间,返回一个指向该存储空间的地址。
- 指针变量名 = new + 类型标识符 (做初始化的值);int *q = new int(5) //开辟一个存放整数的存储空间,并初始化赋值为5;
2、简单应用:
int main()
{
int *p = new int; //未初始化
cout<<"*p未作初始化为:"<< *p <<endl;int *q = new int(5); //初始化为5
cout<<"*q初始化为:"<< *q <<endl;
return 0;
}运行结果:
(二)开辟数组
1、开辟方法:
- 指针变量名 = new +类型标识符[开辟的数组个数] ;int *p = new int [6] // 开辟一个包含6 个单元格的int 型的数组,并且指针指向的是第一个int;
- 指针变量名 = new +类型标识符[开辟的数组个数]()小括号表示做0初始化;
- int *q = new int[20]() //20个值初始化为0的int
- int *q = new int[20] //20个未初始化的int
- string *pp = new string[10] //10个空string
- string *pp = new string[10]() //10个空string,字符类不能用0做初始化,因此仍然表现为未初始化
简单应用:
int main()
{
int *p = new int[5]; //未作初始化,为随机值
for(int i = 0;i<5;i++)
{
cout<<" "<<p[i];
}
cout<<endl;
cout<<"------------------------------------------"<<endl;
int *q = new int[5](); //初始化为0
for(int i = 0;i<5;i++)
{
cout<<" " <<q[i];
}
return 0;
}运行结果:
(三)开辟失败时,则会抛出异常。
(四)可以开辟常量内存块:
const int *p = new const int(10); //一定要做初始化,否则开辟的空间就没有意义
delete p;
const int *p2 = new const int[10]();
delete [ ] p3;
二、delete的用法
(1)释放单个变量内存块
int *p = new int(5);
delete p; //释放单个空间
delete [ ] p; //错误 ,释放单个变量内存时,不用加 [ ];
(2)释放数组
int *q = new int[5]();
delete [] q ;//释放数组时,空括号必须有。
(3)别名定义
typedef int arrT[20]; //arrT是一个包含20个ine的数组的类型别名
int *p = new arrT;//分配一个包含20个int型数据的数组,p指向第一个元素
delete [ ] p; // 方括号必须加,因为我们当初分配的是一个数组
delete释放数组时,空方括号是必须的:它指示编译器此指针指向一个对象数组的第一个元素。如果我们在delete一个指向数组的指针时忽略了方括号或者是在delete一个指向单一对象的指针时使用了方括号,其行为是未定义的。
三、二维数组的开辟与释放
开辟:
int **p = new int *[10]; // 相当于int *p = new int[10][10];
for(int i = 0;i < 10; i++)
{
p[i] = new int [10];
}
释放:
for(int i =0;i< 10;i++)
{
delete [ ]p;
}
四、delete与free的底层实现
class B
{
public:
B()
{
cout << "构造函数" <<endl;
}
~B()
{
cout << "析构函数" <<endl;
}
void *operator new(size_t)
{
B *t =(B*)malloc(sizeof(B));
cout << "内存开辟" <<endl;
return t;
}
void operator delete(void *p)
{
free(p);
cout << "内存释放"<< endl;
return ;
}
};
int main()
{
B * t = new B();//开辟空间时,先调用new运算符的重载函数,开辟内存,再调用构造函数
delete t;//释放内存时,先调用析构函数,再调用delete的重载函数
return 0;
}
运行结果:
因为delete释放内存时,要调用析构函数,所以delete在释放内存时,先开辟的后释放。
TIPS:
free怎么知道释放多少内存的???
与free怎么知道要释放多少内存问题优点类似(详细看上篇博客:【动态内存1】),在申请内存的时候,申请到的地址会比你实际的地址大一点点,它包含了一个存有申请空间大小的结构体。知道了开辟内存的大小,于是就可以准确释放了。(这点不是很正确,看到有误的可以补充或纠正,谢谢啦!)