我们先了解计算机中的内存在使用中被分为下面几部分:
内存布局
栈:也叫堆栈,系统自动分配的空间,只要不特殊声明,系统自动在栈上给变量和函数分配内存。栈是自顶向下增长的
堆:可使用动态内存分配的方式申请堆空间,当数据量较大时,栈空间有限,可在堆上申请空间存放;用完需要手动释放!
常量区:用于存放程序中使用到的常量
代码区:存放可执行代码,在执行过程避免频繁的读取磁盘
作为开发者,我们在程序中只需要管理的就是内存中的堆空间了,在c语言中我们使用一组函数来进行管理:c语言内存管理函数,那么在c++中我们会有更好用的内存管理方式吗?
new和delete的使用
肯定是有的,在c++中存在new和delete这两个运算符专门用于内存管理,那么这两个运算符是如何使用的?
int main()
{
int* pi=new int;
int* pi1=new int(19);
int* arr=new int[10];
int i=0;
for(i=0;i<10;i++)
{
arr[i]=i;
cout<<arr[i]<<" ";
}
cout<<endl;
cout<<"*pi:"<<*pi<<" pi1:"<<*pi1<<endl;
//delete[] arr;
delete arr;
delete pi;
delete pi1;
return 0;
}
运行结果:
上面的关于new的使用:
- 为一个类型数据在堆上分配空间 : new 类型名
- 为一个类型数据在堆上分配空间并初始化为给定初值:new 类型名(初值)
- 为一个数据开辟一定个数的空间: new 类型名 [数据个数]
其中注意int* pi1=new int(19); 是开辟int类型的空间并给初值为19,并不是开辟了19个int类型的空间。
使用完new出来的空间使用完以后就一定要记得使用delete对其分配的空间进行释放:
- 释放单个类型数据 :delete 类型指针名
- 释放整个数组空间:delete[] 数组指针名
注意:对于数组空间的释放,delete的使用稍有不同,当数组的数据类型为基本类型时delete和delete[]都可对其进行释放,但当数组的数据类型为自定义类型时,delete在使用时只释放了arr[0]的空间,那么就不是我们想要的结果了,所以只能用dalete[] 的方式释放。
c++相对于c语言中的内存管理函数有何优势?
new和delete相对于c语言中的malloc、free等函数来讲,new和delete的同时会调用构造和析构函数,并且加入了分配失败的异常机制,这样new和delete在使用起来就非常方便,有大佬模拟了这两个运算符的内部实现,供大家学习一下:new和delete的使用
我们通过学习可以看到new和delete还是通过调用malloc和free来分配和释放内存空间,但是更加便利的是它们会自动调用构造和析构函数。
如果数据类型为基本类型,那么是不需要调用构造函数和析构函数的,只有当数据类型为自定义类型时,才会调用构造函数和析构函数。
当开辟或释放自定义类型的数组数据时,开辟了N个数据时,就调用了N次构造函数和N次析构函数。
new定位表达式
当频繁的分配空间时,我们就会频繁的调用malloc,这样机会导致效率降低;new定位表达式则会提前分配好的空间上调用构造函数初始化,不用再次分配空间,以提高效率。
int main()
{
char* buffer=new char[1024];//内存池
size_t size=0;
Data* pd=new(buffer+size)Data(2015,2,22);//在内存池中分配实例化
size+=sizeof(Data);
Data* pd2=new(buffer+size)Data(2019,2,22);
cout<<(void*)buffer<<endl<<pd<<endl<<pd2<<endl;
pd2->~Data();
pd->~Data();
//delete buffer;
delete[] buffer;
return 0;
}
运行结果:
通过new定位表达式申请的空间实际并没有开辟新的空间,new(指针)类型申请的地址一般是括号里指针保存的地址,然后调用构造函数,所以无需delete释放,如果要释放,可以通过显示调用析构函数的方式释放。例:pd->~Data();
那么如果delete pd之后会出现问题吗?当然,我们在delete pd会将定位表达式分配给pd的那部分内存释放,然而当你在定位表达式适用完之后释放预先分配的空间时,那这块空间就被重复释放。