前言
各位读者朋友们大家好,上期我们讲了类和对象下的内容,类和对象整体的内容我们就讲完了,接下来我们开启新的部分内存管理的讲解。
目录
- 前言
- 一. C/C++内存分布
- 二. C语言中内存管理的方式
- 三. C++内存管理方式
- 3.1 new/delete操作内置类型
- 3.2 new和delete操作自定义类型
- 四. operator new和operator delete函数
- 4.1 operate new和 operator delete函数
- 五. new和delete的实现原理
- 5.1 内置类型
- 5.2 自定义类型
- 5.3 使用不匹配
- 六. 定位new表达式
- 七. malloc/free和new/delete的区别
- 结语
一. C/C++内存分布
C/C++的内存分为堆区、栈区、静态区和常量区,我们先来看下面的一段代码和相关问题:
二. C语言中内存管理的方式
malloc/calloc/relloc的区别:
- malloc是用来在堆区开辟指定字节的函数,返回的是void * 类型的指针。
- calloc函数是用来开辟指定字节空间的函数,并且把开辟的空间的值赋值为0,返回值也是void * 类型的指针。
- realloc函数是用来扩容的函数,第一个形参是要扩容的初始位置的指针,第二个参数是扩容后的空间大小,特别的如果第一个参数传的是空指针,那么realloc函数的功能等同于malloc函数的功能。如果原地扩容成功就返回传过去的指针,如果没有成功就会异地扩容,将原来的数据拷贝到新的位置,再将原位置的空间释放后返回新空间的地址。
这里不需要释放p2,因为p3是在p2的基础上扩容的,如果原地扩容成功,p2和p3指向的空间是同一块,所以只需要释放p3即可;如果异地扩容,realloc函数就已经帮我们将p2释放了。
三. C++内存管理方式
C语言的内存管理方式在C++中可以继续使用,但是有些地方就无能为力,而且使用起来比较麻烦,因此C++又提出了自己的内存管理方式:通过new和delete操作符进行内存管理。
- new 运算符用于在堆(heap)上动态分配内存并初始化对象。它返回指向分配的内存块的指针。
- delete 运算符用于释放先前使用 new 运算符分配的内存。
3.1 new/delete操作内置类型
注意:申请和释放单个元素的空间,使用new和delete操作符,申请和释放连续的空间,使用new[]和delete[],一定要搭配起来使用。
3.2 new和delete操作自定义类型
这样就很方便了,之前我们写链表要去申请节点,现在我们可以这样写:
struct ListNode
{int val;ListNode* next;ListNode(int x):val(x), next(nullptr){}
};int main()
{ListNode* n1 = new ListNode(1);ListNode* n2 = new ListNode(2);ListNode* n3 = new ListNode(3);ListNode* n4 = new ListNode(4);ListNode* n5 = new ListNode(5);n1->next = n2;n2->next = n3;n3->next = n4;n4->next = n5;return 0;
}
这样就写好了一个链表。
在申请自定义类型的空间时,new会调用构造函数,delete会调用析构函数,而malloc和free不会调用。
四. operator new和operator delete函数
4.1 operate new和 operator delete函数
new和delete是用户进行动态内存申请和释放的操作符,operator new和operator delete是系统提供的全局函数,new在底层调用operator new全局函数来申请空间,delete在底层通过operator delete全局函数来释放空间。在c++中new是operator new 加构造函数,delete是operator delete加析构函数
通过上述两个全局函数的实现知道,operator new实际上也是通过malloc来申请空间。如果malloc申请成功就直接返回,否则执行用户提供的空间不足应对措施,如果用户提供该应对措施就继续申请,否则就抛异常。operator delete最终通过free释放空间。
五. new和delete的实现原理
5.1 内置类型
如果申请的是内置类型的空间,new和malloc,delete和free基本类似,不同的地方是:new/delete申请和释放的是单个元素的空间,new[]和delete[]申请和释放的是连续的空间,而且new在申请失败时会抛异常,malloc失败会返回NULL。
5.2 自定义类型
new的原理
1. 调用operator new函数申请空间
2. 在申请的空间上执行构造函数,完成对对象的构造
delete的原理
1. 在空间上执行析构函数,完成对象中的资源清理工作
2. 调用operator delete函数释放对象的空间
new T[N]的原理
1. 调用operator new[]函数,在operator new函数完成对N个对象空间的申请
2. 在申请的空间上执行N次构造函数
new T[N]可以理解为执行N次new
delete []的原理
1. 在释放的对象空间上执行N次析构函数,完成N个对象中的资源清理
2. 调用operator delete[]释放空间,实际在operator delete[]中调用operator delete来释放空间
实际上就是执行N次delete
5.3 使用不匹配
- 内置类型new和free混用
这里不会报错,因为内置类型调用delete时不需要调用析构函数,只调用了_free_dbg也就是free,因此也不会有内存的泄露。但是不建议这样使用。 - 自定义类型new和delete混用
这里调用free相比调用delete来说少调用了析构函数,如果A的析构函数有对内存的释放就会存在内存泄露的风险。 - 内置类型new和delete[] 混用
这里不会有内存泄露的风险,因为new 底层调用的malloc,delete底层调用的free。 - 自定义类型new[]和delete混用
B类中不存在内存的申请和释放,所以不会内存泄漏。
但是下面这种情况程序会崩:
这是为什么?
class A
{
public:A(int a = 0): _a(a){cout << "(int a = 0)" << endl;}A(int a ,int b): _a(a),_b(b){cout << "(int a = 0,int b = 0)" << endl;}~A(){cout << "~A():" << endl;}
private:int _a;int _b;
};class B
{
private:int _b1 = 520;int _b2 = 1314;
};
这里A和B的大小都是8个字节
我们还是从汇编来看:
对于B,编译器给了80个空间
而同样大小,相同个数的A却开了84字节的空间
编译器在给A开空间时多开了4个字节用来存储A的元素个数,而返回的地址却没用从多开的4个字节开始返回,而是往后偏移了4个字节,所以会崩。严格来说两者都应该多开4个字节存元素个数,B没有开是因为编译器优化了,因为编译器看到B没有写析构函数而且没有内存的申请和释放,元素个数是给delete[]用的,当使用delete[]时,指针向前偏移四个字节取到元素个数(释放还是在原位置释放),让编译器知道调几次析构函数。B编译器优化到底,不调用析构函数,所以就不存个数。
如果给B写了析构函数,编译器也会开84个字节的空间:
六. 定位new表达式
定位new表达式是在已分配的原始内存空间中调用构造函数初始化一个对象
使用格式:
new(place_address)type 或者new(place_address)type(initializer_list)
place_address必须是一个指针,initializer_list是类型的初始化列表
上图调用operator new只开了空间并没有初始化,
使用场景:
定位new表达式在实际中一般是配合内存池使用。因为内存池分配出的内存没有初始化,所以如果是自定义类型的对象,需要使用new的定义表达式对其进行显示调用构造函数进行初始化。
七. malloc/free和new/delete的区别
malloc/free和new/delete的共同特点是:都是在堆上申请空间,都需要用户去手动释放。
不同在:
- 1. malloc和free是函数,new和delete是操作符
- 2. malloc申请的空间不会初始化,new可以初始化。
- 3.malloc申请对象时,需要自己计算空间大小并传递,new只需在其后跟上空间类型即可,如果是多个对象,[]中指定个数即可。
- 4.malloc的返回值是void*,在使用时必须强转,new不需要,因为new后跟的是空间的类型。
- 5.malloc申请空间失败时,返回的是NULL,所以使用的时候必须判空,new不需要,但是new需要捕获异常。
- 6.申请自定义类型对象时,malloc/free只会开辟和释放空间,不会调用构造函数与析构函数,而new在申请空间后会调用构造函数完成对象的初始化,delete在释放空间前会调用析构函数完成空间中资源的清理释放.
结语
以上我们就讲完了C++的内存管理,这里还有一些东西没有讲解,需要后续在讲解。感谢大家的阅读,欢迎大家批评指正!