在c和c++中的内存区域主要有四个区域 栈取 堆取 数据段(静态区) 代码段(常量区)
其中栈取主要存储着局部变量(函数参数等)和临时变量(返回值等)他们会随着函数栈帧的创建而创建随着其消失而消失
堆取主要存储的就是动态申请的空间 如c中malloc calloc realloc这些所申请的空间 而在c++中动态申请空间用到的new
静态区存储的是全局变量 和static所修饰的变量 他们的生命周期全局
常量区存储的就是const所修饰的

动态空间的申请
c方式回顾
C语言中动态内存管理用到的是四个函数 malloc/calloc/realloc/free
动态申请是在堆区来开空间

malloc() 只有一个参数 参数为申请空间的大小 单位为字节 通常用sizeof来算出类型的大小再乘个数的方式计算字节数 返回值为void*类型 我们使用时候会根据我们的需要来强转成我们所需要的类型

calloc()有两个参数 第一个参数为申请类型的个数 第二个参数为申请的类型 同样返回值为void*使用时候需要我们手动强转

malloc和calloc的区别在于 malloc申请空间后不会进行初始化 而calloc申请空间后会进行初始化

realloc() 两个参数 第一个参数为指针类型 第二个类型为空间大小单位为字节 用于改变ptr该指针所指向空间的大小 一般在malloc或者calloc之后改变空间时候使用
这里要注意realloc申请空间会处理的一些过程 realloc要将第一个参数所指向的空间的大小修改为第二个参数的大小
realloc使用要注意扩容是原地扩容还是异地扩容
编译器会先检查一下从指针指向位置开始向后够不够开需要的空间(可能会碰到其他空间) 如果够的话就会在原地扩容 而如果空间不够的话 会重新找一个位置来开空间(异地扩容)并把原来位置的空间给释放掉
而如果第一个参数为空指针的话 此时realloc的作用就相当于是malloc

申请空间是在堆区申请 如过空间不够的话会申请失败 所以一般会加上检查返回的指针是否为空 为空的话就perror断言提醒我们 并结束程序 但是通常情况下我们自己写的话也不会出现这种问题
还有动态申请的空间我们要记得手动释放 虽然正常的情况下在程序结束之后 编译器会自动释放动态申请的空间 但是在某些特殊的情况下不会自动释放动态申请的空间就可能造成内存泄漏

free时候要注意不要重复free同一块空间否则会崩溃 如以上例子 给a扩容之后把c给了a a就和c指向同一块空间 只需free一次
c++的方式
在c++中创造了 new和delete来分别申请和释放空间 new和delete是关键字
c++要创造新的 不用c中原来的 自然因为new和delete有他们的优点
一个是使用起来更方便不用强转 算空间大小 对内置类型也可以自定义初始化
但是主要的优点是在申请自定义类型空间的时候 new在申请空间的同时会自动调用构造函数进行初始化 delete再释放空间的同时会先自动调用他们的析构函数
new和delete使用方式
在new后面加要存储的类型 然后直接给了对应的指针类型 不需要算大小不需要强转
如果要初始化在类型后面再一个小括号里面为初始化的值
如果要申请多个类型的空间 则在类型后面加中括号 里面为申请的个数 对多个数据初始化的话 则再往后面加个大括号里面依次初始化
delete释放时候只需要在后面再指针变量名就可以对于于同时申请多个的 要在变量前面加一个中括号

而对于申请自定义类型的空间 从下面的代码知道确实使用new的时候会自动调用构造函数 用delete的时候会自动调用析构函数

对于申请多个类空间的初始化
第一个是直接通过默认构造函数来初始化的
第二个是通过先构造 再拷贝构造初始化的
第三个本质和第二个一样 先构造再拷贝 不过这里用到了匿名对象 连续的构造+拷贝编译器直接优化为了直接构造
第四个之前学过隐式类型转化 这里int先隐式通过A的构造函数转换为A类型再经过拷贝构造 连续构造+拷贝构造编译器会优化为直接构造

申请失败情况
在c中申请空间之后我们一般都要断言检查一下是否申请成功了
在c++中new申请失败了后是通过异常捕获 抛异常的方式

如果一直动态申请空间就会报出以下的问题

扩容问题
在c中扩容用到的realloc 在c++中怎么扩容呢
c++中没有扩容的函数 只能还用new来解决 用一个同类型的tmp来开原来二倍的空间再把原来的值拷贝到新的空间中 再把原来的空间给释放掉 让原来的指针arr指向现在的新空间

operator new 和operator delete
new和delete是用户进行动态内存申请和释放的操作符,operator new 和operator delete是 系统提供的全局函数,new其实就是调用的operator new来申请空间的 同样delete也是通过operator delete释放空间的
而operator new 实际也是通过malloc来申请空间,如果 malloc申请空间成功就直接返回,否则执行用户提供的空间不足应对措施,如果用户提供该措施 就继续申请,否则就抛异常。operator delete 最终是通过free来释放空间的。
new和delete的实现原理
对内置类型而言其实用c++的new delete和c的malloc free 差不了多少 唯一注意的是在c++delete的时候 要根据申请时的情况 看用 delete [] +指针变量 还是直接delete+指针变量 然后就是new失败会抛异常 而c中malloc申请失败是返回为NULL的方式
主要的区别就是在自定义类型上new自动调用构造 delete自动调用析构
new的原理 1. 调用operator new函数申请空间(就是通过malloc实现) 2. 在申请的空间上执行构造函数,完成对象的构造
delete的原理 1. 在空间上执行析构函数,完成对象中资源的清理工作 2. 调用operator delete函数释放对象的空间(其实就是通过了free)
所有如果new申请的空间用free来释放相当于就跳过了进行析构函数的操作 造成了内存泄漏
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其实申请空间调用的是operator new 而其调用的又是malloc new[]其实就是调用的就是operator new[] 也就是多次调用了operator new 也就是多次调用了malloc delete也是同样的道理
那么对于以下的内置类型它开空间就是用的malloc的逻辑 不是自定义类型没有进行构造 在析构delete其实就是调用的free 这里free也可以和delete一样 只不过没有加[] 会少析构了 但是少析构几次没出现什么问题

对于自定义类型 以下两个对A类类型和B类类型的操作一样都在delete的时候没有加[] 但是 对于B的不会崩溃 而对于A的崩溃了
简单来说就是 如果有析构函数的话(这里的A) 编译器就会在开空间的时候额外在第一个位置的前一个位置额外开4个字节的空间来存储个数 然后会重新指向第一个位置 在释放时候 因为指向的位置前面还有空间 它指向中间的位置 会出问题
而对于没有写析构函数(这里的b)编译器就会就行优化 把前面存个数的4个字节省了 这样就是正常的

其实正常情况下只要我们写的时候把各自匹配好了 也就不会出什么问题 注意new的用delete的匹配 申请时候用了[] 在delete的时候也记得加就行
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在释放空间前会调用析构函数完成 空间中资源的清理释放

743

被折叠的 条评论
为什么被折叠?



