💐 🌸 🌷 🍀 🌹 🌻 🌺 🍁 🍃 🍂 🌿 🍄🍝 🍛 🍤
📃个人主页 :阿然成长日记 👈点击可跳转
📆 个人专栏: 🔹数据结构与算法🔹C语言进阶🔹C++
🚩 不能则学,不知则问,耻于问人,决无长进
🍭 🍯 🍎 🍏 🍊 🍋 🍒 🍇 🍉 🍓 🍑 🍈 🍌 🍐 🍍
文章目录
- 一、C语言中的内存管理方式
- 二、C++中的内存管理方式
- 1、new/delete操作内置类型
- 2.new/delete操作自定义类型
- 三、🔆operator new与operator delete函数(重点)
- 1.通过反汇编看底层
- 2.透过源码分析两个全局函数
- 1)==operetor new==
- 2)==operetor delete==
- 四、🚩再次理解new/delete
- 1.new的原理
- 2.delete的原理
一、C语言中的内存管理方式
在C语言中,内存的管理方式 有【malloc 、calloc、realloc】这几种动态开辟内存的方式。
那你还记得他们的区别吗?
malloc
用于分配指定大小的未初始化内存块,其不会对申请出来的内存块做初始化工作
calloc
用于分配指定数量和大小的连续内存块,并将其初始化为0
realloc
用于重新分配内存块的大小,并尽可能保留原有数据。其有两种扩容机制,分别为【本地扩容】和【异地扩容】
二、C++中的内存管理方式
C语言内存管理方式在C++中可以继续使用,但有些地方就无能为力,而且使用起来比较麻烦,因此C++又提出了自己的内存管理方式:通过
new和delete
操作符进行动态内存管理。
1、new/delete操作内置类型
- 接下去就让我们来看在C++中如何使用new这个关键字来动态申请空间
1). 动态申请一个int类型的空间
int* p1 = new int;
2).动态申请一个int类型的空间并初始化为10
int* p2 = new int(10);
3).动态申请10个int类型的空间
int* p3 = new int[3];
- 那既然申请了,我们就要去释放这些空间,C语言中使用free,但是在C++中呢,我们使用delete,对于普通的空间我们直接delete即可,但是对于数组来说,我们要使用
delete[]
delete p1;
delete p2;
delete[] p3;
进行调试可以发现:
- 对于单块的内存区域,只需要使用new 数据类型(初始化数值)的方式即可;而对于像数组这样的空间,我们要使用new int[5]{初始化数值}的形式去进行,此时才可以做到一个初始化
2.new/delete操作自定义类型
学习完了使用new/delete去操作C++中的
内置类型
,接下去我们来看看我们要如何去操作一个自定义类型
通过下面一个例子,你就会知道C++中的new,delete用起来有多爽了。🐶
- 在之前C语言中学习链表时我们新建结点。由于使用malloc函数,每次新建结点需要进行调用,安全检查等等操作。
对比1:
struct ListNode {
int val;
struct ListNode* next;
};
struct ListNode* BuyListNode(int x)
{
struct ListNode* node = (struct ListNode*)malloc(sizeof(struct ListNode));
if (NULL == node)
{
perror("fail malloc");
exit(-1);
}
node->val = x;
node->next = NULL;
return node;
}
struct ListNode* n1 = BuyListNode(1);
struct ListNode* n2 = BuyListNode(2);
struct ListNode* n3 = BuyListNode(3);
- 使用new/delete:并且使用之前所学习过的构造函数初始化列表在开辟出空间的时候就做一个初始化的工作
对比2:
struct ListNode {
int val;
struct ListNode* next;
ListNode(int x)
: val(x)
, next(NULL)
{}
};
ListNode* n4 = new ListNode(1);
ListNode* n5 = new ListNode(2);
ListNode* n6 = new ListNode(3);
- cv对比1,2就可以很明显的发现,使用new减少了不少代码量,而且还提升了安全性。
- 📋通过上面的调试,我们可以知道在C++中使用new是会去自动调用构造函数并完成初始化的。
- 📋而且,new几个对象,就会调用几次构造函数,delete 也一样。
- 注意点:
malloc
出来的一定要用free
,而new
出来的一定要用delete
三、🔆operator new与operator delete函数(重点)
1.通过反汇编看底层
new和delete是用户进行动态内存申请和释放的操作符,operator new 和operator delete是系统提供的全局函数,new在底层调用operator new全局函数来申请空间,delete在底层通过 operator delete全局函数来释放空间。
通过反汇编,可以清楚的看出new 的底层还是调用了operator new函数。所以我们想要弄清楚new的原理,就需要搞明白operator new 函数。
- 这里我们需要关注的点有两个,即这两个call指令的调用,分别是调用【operator new】从堆区去开空间和调用【A::A】这个构造函数去进行初始化工作
这里我们需要关注的点也有两个,即这两个call指令的调用,分别是调用【A::~A】去析构函数释放资源和调用【operator delete】这个函数去释放从堆区申请的空间。不过呢,它们这两个部分被编译器做了一个封装,在外层我们还需用通过一个call指令和jmp指令去做一个跳转,才能看到底层的这块实现
2.透过源码分析两个全局函数
1)operetor new
- 通过查看它的源代码我们可以发现其内部还是使用【
malloc
】去堆中申请空间的
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);
}
2)operetor delete
接下去的话是是operator delete,仔细去观察的话可以看出这段代码有调用到一个_free_dbg()这个函数,它其实就是我们在C语言中所写的free()函数,那么就可以得出其实这个函数底层也和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->nBlockUse));
_free_dbg(pUserData, pHead->nBlockUse);
__FINALLY
_munlock(_HEAP_LOCK); /* release other threads */
__END_TRY_FINALLY
return;
}
四、🚩再次理解new/delete
1.new的原理
- 1.调用operator new函数申请空间
2.在申请的空间上执行构造函数,完成对象的构造
2.delete的原理
- 1.在空间上执行析构函数,完成对象中资源的清理工作
2.调用operator delete函数释放对象的空间
下面这幅图很好解释了他们之间的关系
📜补充:使用new/delete发生异常时,会进行抛出异常。