目录
C++内存管理方式
new/delete操作内置类型:
new/delete操作自定义类型:
new和delete的底层实现:
operator new与operator delete函数
定位new
内存泄漏
动态内存管理,早些我们接触C语言的时候就已经在很熟练的游玩在堆上开空间的操作了,我们最开始的应用莫过于数据结构顺序表的实现,其中的扩容我们已经再熟悉不过了
//扩容
if (slt->size == slt->capacity)
{
int newcapacity = slt->capacity == 0 ? 4 : slt->capacity * 2;
Datatype* tmp = (Datatype*)realloc(slt->a, newcapacity * sizeof(Datatype));
if (realloc == NULL)
{
perror("realloc fail!");
exit(-1);
}
slt->a = tmp;
slt->capacity = newcapacity;
}
但是我们总是绕不开开辟完毕的返回值需要判空的问题
那么C++作为C的升级,在动态内存管理上是否有什么讨喜的升级呢?
C++内存管理方式
new/delete操作内置类型:
C++中开辟内存空间的关键字升级成了new,相较于C语言较为繁琐的申请格式,new则简单粗暴的多,其释放也非常简单。
有如下三种使用new的方式
void Test()
{
// 动态申请一个int类型的空间
int* ptr4 = new int;
// 动态申请一个int类型的空间并初始化为10
int* ptr5 = new int(10);
// 动态申请10个int类型的空间
int* ptr6 = new int[3];
delete ptr4;
delete ptr5;
delete[] ptr6;
}
new和delete的格式一定要对齐,也就是如果使用了new[]就必须对应着使用delete[]
new/delete操作自定义类型:
对于自定义类型,new会直接调用其构造函数,delete会调用其析构函数,而malloc和free则不会。
malloc过大的空间的时候会直接崩溃,而new则是抛异常,由于牵扯到多态和继承的问题,不记述。
示例:
new和delete的底层实现:
new和delete是用户进行动态内存申请和释放的操作符,operator new 和operator delete是系统提供的全局函数,new在底层调用operator new全局函数来申请空间,delete在底层通过operator delete全局函数来释放空间。
既然new是调用operator函数来实现的,那么这个全局函数又是怎么实现的?
operator new与operator delete函数
我们来看看原码
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
// 如果申请内存失败了,这里会抛出bad_alloc 类型
static const std::bad_alloc nomem;
_RAISE(nomem);
}
return (p);
}
/*
operator delete: 该函数最终是通过free来释放空间的
*/
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->nBlo
_free_dbg(pUserData, pHead->nBlockUse);
__FINALLY
_munlock(_HEAP_LOCK);
__END_TRY_FINALLY
return;
}
原码虽然看上去很复杂,但是我们也能在里面找到一些老熟人,比如malloc和free,这下我们知道了,new和delete的底层实现也是依靠于malloc和free实现。
那么问题来了,C++弄这么大一坨封装malloc的意义在哪?
答:封装malloc的意义则是申请内存失败就抛异常
那么总结一下new对内置类型和自定义类型的实现原理
对于内置类型:
如果申请的是内置类型的空间,new和malloc,delete和free基本类似,不同的地方是:
new/delete申请和释放的是单个元素的空间,new[]和delete[]申请的是连续空间,而且new在申请空间失败时会抛异常,malloc会返回NULL。
对于自定义类型:
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
定位new可以直接调用某一个类里的构造函数,但是它需要一个地址以及其对象的类。
那么如何销毁这块空间呢?
析构函数可以显式调用,可以直接调。
直接delet这块空间也是可以的,因为delete的底层依旧是free和调用析构函数,本质上和上面两段代码没有本质区别
当然以上的开辟方法其实还是完全不敌new。
内存泄漏
内存泄漏的本质我个人更加愿意理解为空间的遗忘,内存泄漏并不是指内存在物理上的消失,而是应用程序分配某段内存后,因为设计错误,失去了对该段内存的控制,因而造成了内存的浪费。
虽然说对正常结束的程序影响不大,但是对长期运行的程序就是定时炸弹。被遗忘的空间会越来越大,最终导致占用过多的内存导致程序崩溃
如何避免内存泄漏
1. 工程前期良好的设计规范,养成良好的编码规范,申请的内存空间记着匹配的去释放。ps:
这个理想状态。但是如果碰上异常时,就算注意释放了,还是可能会出问题。需要下一条智
能指针来管理才有保证。
2. 采用RAII思想或者智能指针来管理资源。
3. 有些公司内部规范使用内部实现的私有内存管理库。这套库自带内存泄漏检测的功能选项。
4. 出问题了使用内存泄漏工具检测。ps:不过很多工具都不够靠谱,或者收费昂贵
以上就是C++中的动态内存管理的概述了!希望对你有点帮助!感谢阅读!