前言
在内存的应用中,我们继续深入展开。内存是如何开辟的?我们知道在编写程序之后,编译器会自动的给变量、常量数据、静态数据等给予对应的内存空间作为储存,而当我们的程序稍微复杂一点,需要进行有意识的根据不同场景开辟不同大小的空间去使用,该如何实现呢?因此在这里我们有一个概念叫做空间的动态开辟,在C语言中我们会使用malloc、calloc或者realloc去进行一定大小空间的开辟,使用free在最后对开辟的空间进行释放。这些方式在C++中同样适用,但是在有一些特殊的地方使用起来往往比较麻烦有时甚至无法使用(比如在对自定义类型使用时无法调用构造函数与析构函数,从而造成随机值产生以及内存泄漏情况),因此C++便提出了属于自己的内存管理方式,通过new和delete操作符进行动态内存的管理。
通俗的意义上来说,new表示申请一段空间,delete表示释放一段空间,两者成对出现。
new使用方法
操作内置类类型
1.动态申请一个数据类型的空间
数据类型* + 指针变量名称 = new +变量类型
int* a = new int;//申请一个int型数据大小的空间
2.动态申请一个指定数据类型的空间并初始化
数据类型* + 指针变量名称= new +变量类型(val)
int* b = new int(20);//申请一个int型数据大小的空间,并附初始值为20
3.动态申请若干个指定数据类型的空间
数据类型* + 指针变量名称 = new +变量类型[number]
int* c = new int[10];//申请10个int型数据大小的空间,初始值为随机值
【注意】使用new时,()表示开辟完后设置初始值,[]表示开辟若干数量个空间,二者不可以同时使用
操作自定义类型
1.申请单个自定义类型的空间
类型名* +指针变量名=new 类型名
Class_name* p1=new Class_name;
2.申请多个自定义类型的空间
类型名* +指针变量名=new 类型名[number]
Class_name* p1=new Class_name[n];
delete使用方法
1.释放单个的空间
delete + 指针变量名称
delete a;
2.释放连续的空间
delete+[]+指针变量名称
delete[] c;
【注意】new在delete使用时成对出现,molloc、realloc以及calloc与free搭配使用,new与delete搭配使用,不能混合使用。
malloc与new的主要区别
- malloc是申请空间的函数,free是操作符
- malloc只是申请一部分空间,不对空间做任何处理,new申请空间的同时进行初始化
- malloc申请空间需指定字节大小,new会根据类型自动计算
- malloc申请的空间需要制定类型,因为其返回值是void*,new不需要,其返回值类型就是类型指针
- malloc申请失败返回NULL,需要手动判断是否开辟成功,new申请失败会捕捉异常
- 在对自定义类型进行申请时,malloc与free只会开辟空间不会调用构造函数与析构函数,因此并不会进行初始化,同时free只会释放空间不会对资源进行清理,而new会对对象进行初始化,delete在释放空间时同时对资源进行清理。
- new进行连续申请时,若没有默认构造函数则无法进行。
代码如下,进行对比:
#include<iostream>
using namespace std;
class Grade
{
public:
Grade(int math = 60, int chinese = 60, int english = 60)
:_Math(math),
_Chinese(chinese),
_English(english)
{}
private:
int _Math;
int _Chinese;
int _English;
};
int main()
{
Grade St(90,90,90);//对象实例化
Grade* st1 = new Grade;//构建单个对象,观察是否调动构造函数
Grade* st2 = new Grade(10, 20, 30);//构建初始化对象
Grade* st3 = (Grade*)malloc(sizeof(Grade));//使用malloc,观察是否调用构造函数
Grade* arr_st = new Grade[10];//构造多个对象
Grade* arr_st1 = (Grade*)malloc(sizeof(Grade)*10);//使用malloc构造多个对象
//观察是否调用析构函数
delete st1;
delete st2;
free(st3);
delete[] arr_st;
free(arr_st1);
cout << "end" << endl;
return 0;
}
operator new和operator delete
首先要清楚,operator new和operator delete并不是运算符的重载,而是系统提供的全局函数,new在底层调用operator new全局函数来申请空间,delete在底层通过operator delete全局函数来释放空间。在底层,它们党的使用方式类似于malloc和free。
例如需要申请一个整型的空间,以及其对应释放
int* pq=(int*)operator new(sizeof(int));
operator delete(pq);
下面我们来观察一下其源码如下:
//operator new 的代码实现
void *__CRTDECL operator new(size_t size) _THROW1(_STD bad_alloc)
{
// try to allocate size bytes
void *p;
while ((p = malloc(size)) == 0)
if (_callnewh(size) == 0)
{
// report no memory
//如果申请失败则抛出错误
static const std::bad_alloc nomem;
_RAISE(nomem);
}
return (p);
}
//operator delete 的代码实现
void operator delete(void *pUserData)
{
_CrtMemBlockHeader * pHead;
RTCCALLBACK(_RTC_Free_hook, (pUserData, 0));
if (pUserData == NULL)
return;
_mlock(_HEAP_LOCK); /* block other threads */
__TRY
/* get a pointer to memory block header */
pHead = pHdr(pUserData);
/* verify block type */
_ASSERTE(_BLOCK_TYPE_IS_VALID(pHead->nBlockUse));
_free_dbg(pUserData, pHead->nBlockUse);
__FINALLY
_munlock(_HEAP_LOCK); /* release other threads */
__END_TRY_FINALLY
return;
}
由此我们可以看出operator new实际上依旧是通过malloc来申请空间,如果不成功,则抛出相对应的错误,并且执行相关用户提供的措施,而operator delete最后还是通过free来进行空间的释放,只是其中的过程更加详细。
所以我们可以知道new与delete实际上的工作原理其实并不复杂,在内置类型的申请(释放)上new(delete)和malloc(free)是一样的,只不过,new在失败时会抛出异常;而在进行自定义类型空间申请(释放)时,会调用operator new(operator delete)函数,之后在执行构造函数(析构函数)
欢迎大家批评指正,共同进步。