C++之动态内存管理

想必很多学习C++的人都有很深厚的C语言基础.
在C语言中,我们是用malloc,calloc,realloc三位男士和free一位女士来动态管理内存的
先coding一下C语言的基本动态内存管理模式
void RAMManageByC()
{
int ptr1=(int)malloc(sizeof(int));//申请大小为sizeof(int)大小的内存,不进行初始化
int* ptr2=(int*)malloc(sizeof(int)n);//申请长度为n的整形数组,不进行初始化
int
ptr3=(int*)calloc(n,sizeof(int));申请n个大小为sizeof(int)的整形变量,并且将其初始化为0
int* ptr4=(int*)realloc(ptr2,sizeof(int)n);//在ptr2后边继续申请n个sizeof(int)的空间,不进行初始化
free(ptr2);
free(ptr3);
free(ptr4);//这里不需要释放ptr1,因为释放ptr4的同时也已经释放了ptr1所申请的内存空间,如果再释放,就会导致二次释放的问题
}
然而这种初始化方式是有很大的缺陷的
首先它的初始化非常的难受,malloc申请的内存空间需要手动去初始化,calloc申请的内存空间虽然名义是初始化的,但是它不一定能按照我的意愿去进行初始化,realloc和malloc相同,这大大降低了我们程序猿的幸福感
然而,这还不是最坑的,请看下边的栗子
class Date{
public:
Date(int year,int month,int day)
: _year(year),_month(month),_day(day)
{}
~Date()
{}
private:
int _year;
int _month;
int _day;
};
void testC()
{
Date ptr=(Date)malloc(sizeof(Date));
//然而这里它仅仅是申请了sizeof(Date)这麽大的空间,并没有对其内部成员进行初始化操作,非常之难受
}
现在我们既然来到了C++世界,就让我们看下一种新的动态内存管理的方式
void RAMManageByCPlusPlus()
{
int
ptr1=new int;//申请一个int类型变量的内存空间,不对其进行初始化操作
int* ptr2=new int(3);//申请了一个int类型的变量的内存空间,并将其大小初始化为3
int* ptr3=new int[3];//申请一个长度为3的int类型数组,不进行初始化
//唯一尴尬的就说new在申请连续内存的时候无法进行初始化
delete ptr1;//释放单个内存空间
delete ptr2;
delete[] ptr3;//释放连续的内存空间
}
对内置类型的变量申请内存说完以后,我们再来谈谈自定义类型
就比如对上面的日期类
void testCPlusPlus()
{
Date* ptr=new Date(2020,3,20);//申请一个Date类型的变量,并调用构造函数对其进行初始化
Date* ptr1 = new Date[10];
//申请一个大小为10的Date类型的数组
//然而,这里编译器是会报错的
//为啥呢,因为用new来申请内存空间的时候,是会调用默认构造函数的,而Date类事实上并没有默认构造函数,所以程序势必凉凉.
delete ptr;//先调用析构函数释放资源,然后再回收对象空间
datele []ptr1;//和上边相同
}
事实上对于我们的自定义类型,new和delete在管理内存的时候,会调用构造函数/析构函数,而malloc和free则不会
对于malloc,free,new,delete的使用需要注意的是
1.malloc/free,new/delete在使用上一定要对应起来
2.malloc和delete在使用时候,类似于函数调用,而我们的new /delete却不然
,事实上,new 和 delete只是关键字,它们并不是函数
我接下来在介绍两个重要的全局函数
void* operator new(unsigned int n)
void operator delete(unsigned int n)
值得一提的是,他们不是运算符重载函数
而是系统全局函数
事实上new和delete在底层就说通过这两个函数来实现内存申请和销毁工作的
当然我们也可以直接调用他们来完成内存的申请和释放工作
下边我们先研究一哈void* operator new(size_t n)//size_t和unsigned int一个意思,这个想必大家都知道
比如说申请以一个大小为sizeof(int)的空间
int* ptr=(int*)malloc(sizeof(int))
int* ptr1=(int*)operator new(sizeof(int))
这个使用方法确实和malloc一个模打出来的
事实上,它的底层就是调用malloc函数来完成的
我们都知道malloc函数调用失败的时候会返回NULL
但是operator new并不是这样的
它当malloc申请内存失败时会尝试空间不足的应对措施,如果该应对措施可以解决内存不足的问题,那么它会继续尝试申请,否则会抛出异常
对于new申请自定义类型变量,它走的流程是:
operator new—>构造函数
下边我们说一下void operator delete(void*)
正如大家所想象的那样子,delete也是一个全局函数,它的底层实际上是调用了free
对于自定义类型对象的释放,当我们用delete释放自定义类型对象时走的流程就是析构函数 ->> void operator delete(void*)
写到这里,我们可以发现一个问题
就是我们每次申请内存都要消耗时间,当然申请次数少不要紧,频繁的话实际上是会影响性能的
事实上计算机给我们想了一种特别好的办法去解决这个问题
就说内存池的思想
什么是内存池呢,事实上,内存池就说一个备胎,计算机并不是一次只申请你所要的那些内存,而是申请了很多,当你用的时候再从池子里边拿就可以,这样就大大的优化了内存申请的开销,我们都知道链表是经常会申请内存的,现在我写一个链表的类operator new重载函数帮助理解一下内存池的思想
class Node{
Node()
{}
void* operator new(size_t n)
{
//内存池申请
void* ptrallocator().allocate(1);
return ptr;
}
//内存池释放
void operator new(void* p)
{
allcator().deallocate((Node* ptr,1));
}
};
我再说一个显示调用构造函数的方法
void test()
{
Date* ptr=(Date*)malloc(sizeof(Date));//申请内存空间
new (ptr) Date(2020,10,30);//完成对自定义类型的初始化
语法就是new(初始化空间的地址)类型(参数列表)
}
最后再说一下申请了内存不是放的后果(内存泄漏)
我们写的程序会在运行结束的时候去释放空间资源,对于运行时间很短,而且申请空间不大的程序来说,比如说我们经常写的程序,这确实不是很要紧,但是对于一个服务器来说,它实际上是全天24小时运行的,如果有内存泄漏,就会造成内存不够用的情况,从而导致服务器程序彻底崩溃,因此内存泄漏在实际工作中是绝对不可以发生的,因此我们在使用new malloc申请的内存一定要及时的释放

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值