要学习C++的内存管理,首先我们需要回顾一下C语言的内存管理,
1.回顾C语言的内存管理
我们现在需要理解C语言的四块内存区域。
栈: 又叫堆栈-非静态局部变量/函数参数/返回值等等,栈是向下增长的。
堆: 用于程序运行时动态内存的分配,堆是可以向上增长的。
数据段:存储全局数据和静态数据
代码段:可执行的代码/只读常量
让我们看看什么样的数据存储在这些内存空间中
2.C语言中动态内存管理方式
C语言中动态内存的管理方式需要用到几个函数:malloc / calloc / realloc / free
1.malloc
malloc的用法一般形式为:类型名*指针名 = (类型名*)malloc(需要开辟空间的字节大小)
int*ptr=(int*)malloc(sizeof(int));
2.calloc
calloc的用法一般形式为:类型名*指针名=(类型名*)calloc(所要开辟类型空间个数,每个类型空间的大小)
int* ptr=(int*)calloc(6,sizeof(int));
3.realloc
realloc的用法一般形式为: 类型名*指针名2=(类型名*)realloc(指针1,所需字节个数空间大小)
realloc一般是用来扩大一个已经存在的动态内存空间的
如上图,我们要扩大指针1指向的动态内存空间,那我们就给realloc指向这个空间的指针和要扩大至想要空间的大小,再用指针2去接收这块空间的地址。
特殊情况:当扩大的空间和已经使用的空间重叠时,操作系统会重新再找一块空间来复制这块空间的数据
4.free
free函数用于动态内存空间的释放,因为我们开辟空间都是在堆中开辟,如果我们一直开辟空间,那么迟早堆里面的空间会被使用完,所以我们需要使用free函数来把这块空间释放给操作系统
free用法一般形式free(指向空间的指针名)
int* ptr= (int*)malloc(sizeof(int) * 6);
free(ptr);
当我们在堆中开辟了一块空间,之后我们不使用它了,我们一定要释放,不然会造成内存泄漏。
3.C++内存管理方式
C语言中的内存管理方式也可以放到C++中使用,但有些时候可能不太适合而且很麻烦,所以C++提供了自己的内存管理方式,通过两个操作符进行管理:new和delete。
1.new
#include<iostream>
using namespace std;
int main()
{
int* ptr2 = new int;
int* ptr1 = new int(10);
int* ptr = new int[5] {0, 1, 2};
}
上述new是和malloc类似,也是开辟空间
第一个是开辟一个存放int类型数据的空间并且用ptr2指向这个空间
第二个是开辟一个存放int类型数据的空间,并且将这int类型的数据初始化为10用ptr1指向
第三个是开辟一个大小为5的数组,并且数组前三个用{}里的数据对应初始化,其它没有初始化的数据都为0
2.delete
那么上面开辟了空间就要释放,C++提供了一个操作符delete来释放new出来的空间
#include<iostream>
using namespace std;
int main()
{
int* ptr2 = new int;
int* ptr1 = new int(10);
int* ptr = new int[5] {0, 1, 2};
delete ptr2;
delete ptr1;
delete[]ptr;
}
上述就是释放空间的格式,需要注意数组的初始化有所不同,要在指针和delete中间加[ ]
3.new和malloc的区别
对于内置类型的数据来说,malloc和new的差别不大,new和malloc的区别主要时在用于自定义类型有所差别。
我们看下面代码
#include<iostream>
using namespace std;
class A
{
public:
A()
{
cout << "A()" << endl;
}
~A()
{
cout << "~A()" << endl;
}
private:
int aa;
};
int main()
{
A* ptr1 = new A;
}
当new用于自定义类型时对调用自定义类型的构造函数,
看反汇编代码
当我们使用new来开辟空间时,new会先调用operator new(0C81140h)再调用这个自定义类型的构造函数。
让我们执行一下代码看看
通过运行结果我们发现,用new开辟空间时会调用构造函数。
那么operator new又是什么呢
我们可以看到,调用operator new时也是调用malloc来开辟空间,但与malloc有所不同的是,当开辟空间失败时,new会抛出bad_alloc 类型异常,而malloc在开辟空间失败会返回一个NULL
4.delete和free的区别
对于内置类型来说delete与free差别也不大,但是对于自定义类型来说,当使用delete,delete会先调用析构函数处理掉类型的数据,再释放空间。
让我们看以下代码
#include<iostream>
using namespace std;
class A
{
public:
A()
{
cout << "A()" << endl;
}
~A()
{
cout << "~A()" << endl;
}
private:
int aa;
};
int main()
{
A* ptr1 = new A;
delete ptr1;
}
运行结果
可以看出使用new和delete分别调用了构造函数和析构函数
我们看看反汇编代码
我们可以看到delete先是调用了析构函数再调用了operator deletor()
那么这个operator delete里面有什么呢
这个operator delete里面其实就是free函数
4.总结
通过上面的学习后,我们总结一下new/deletehe对不同类型实现原理的区别:
1.对内置类型
1.如果申请的是内置类型的空间,new和malloc,delete和free基本类似,不同的地方是: new/delete申请和释放的是单个元素的空间,new[]和delete[]申请的是连续空间,而且new在申请空间失败时会抛异常,malloc会返回NULL。
2.对自定义类型new的原理
1.调用operator new函数申请空间;
2.在申请的空间上执行构造函数,完成对象的构造;
3.对自定义类型newT[N]的原理
调用operator new[]函数,在operator new[]中实际调用operator new函数完成N个对
象空间的申请。
4.在申请的空间上执行N次构造函数
3.对自定义类型delete的原理
1.在空间上执行析构函数,完成对象中资源的清理工作。
2.调用operator delete函数释放对象的空间。
3.对自定义类型delete[]的原理
在释放的对象空间上执行N次析构函数,完成N个对象中资源的清理。
调用operator delete[]释放空间,实际在operator delete[]中调用operator delete来释
放空间。
————————————————版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
原文链接:https://blog.csdn.net/weixin_67596609/article/details/130722926
最后,总结出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在释放空间前会调用析构函数完成空间中资源的清理。
————————————————版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
原文链接:https://blog.csdn.net/weixin_67596609/article/details/130722926