C/C++内存分布和管理
通过C/C++语言的学习也了解到内存的分布以及管理,所以C/C++内存划分分为:栈区、堆区、静态区、常量区等等。而我们在写代码的时候应该存放在那个区域呢?临时变量、函数等一般存放在栈,动态申请的空间则存放在堆区,全局变量、static修饰的变量和函数存放在静态区,常量则存放在代码段(常量区)。
目录
C/C++内存分布
C/C++内存管理方式
new和delete操作符
operator new与operator delete函数
new 和delete实现原理
定位new表达式
总结
malloc、free和new、delete的区别
C/C++内存分布
栈又叫堆栈--非静态局部变量/函数参数/返回值等等,栈是向下增长的,堆用于程序运行时动态内存分配,堆是可以上增长的,数据段--存储全局数据和静态数据,代码段--可执行的代码/只读常量。
C/C++内存管理方式
C语言中动态申请空间的方式是使用函数:malloc、calloc、realloc,而释放空间是使用:free函数,对于C++就不一样了,在C++中动态申请空间使用的是操作符new和释放空间操作符delete。
在C语言中这三个申请动态空间的函数有什么区别呢?malloc函数在申请空间后并不会初始化,而是随机数,calloc则在申请空间成功后进行初始化,realloc则是对一块空间进行扩容,而扩容有两种情况:第一种情况是进行扩容的那块空间后面空间足够,直接对原来那块空间进行扩容。第二种情况是原来空间不足,则另寻一处重新开辟一块所需大小的空间,原来空间会销毁。释放空间就需要使用free来释放空间。
new和delete操作符
对于C语言中动态申请空间的方式在C++中还可以继续使用,但是与C++中动态空间申请的方式有些地方显得无能为力,所以C++提出了自己的内存申请方式。
示例:
int main()
{int* p1 = new int;//不初始化int* p2 = new int(1);//初始化为1int* p3 = new int[5];//申请五个int类型的空间,没有初始化int* p4 = new int[5]{ 0,1,2,3,4 };//申请五个int类型的空间,初始化为0,1,2,3,4delete p1;delete p2;delete[]p3;delete[]p4;return 0;
}
new操作符使用为:new + 类型 ,【】里是个数,当只申请一个空间时不需要写,初始化时使用(),当申请多个空间使用【】,初始化为{},未给值的空间初始化为0。
注意:在释放多个空间时需要在delete后加【】,new和delete需要匹配使用。
对于自定义类型是怎么样的呢?
示例:
class A {
public:A(int n = 1) :_a(n){cout << "A(int n)" << endl;}A(const A& d){_a = d._a;cout << "A(const A& d)" << endl;}~A(){cout << "~A()" << endl;}
private:int _a;
};int main()
{int* p1 = (int*)malloc(sizeof(int) * 4);free(p1);A* p2 = new A(0);delete(p2);return 0;
}
运行结果:
对于自定义类型我们通过运行结果可以发现调用了构造函数和析构函数,在申请自定义类型的空间时,new会调用构造函数,delete会调用析构函数,而malloc与free不会。这就是C++和C语言申请空间最大的区别,对于内置类型两者差不多,自定义类型就大不相同。
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实现原理
如果申请的是内置类型的空间,new和malloc,delete和free基本类似,不同的地方是:new/delete申请和 释放的是单个元素的空间,new[]和delete[]申请的是连续空间,而且new在申请空间失败时会抛异常, malloc会返回NULL。
如果是自定义类型:
new = operator new + 构造函数,delete = 析构函数 + operator delete。
new【N】原理:1、调用operator new【】函数,在operator new【】函数中调用operator new函数来完成N个对象空间的申请。2、在申请的空间上进行N次构造。
delete【】原理:1、在释放对象空间上调用N次析构函数,完成N个对象资源清理。2、调用operator delete【】函数,在operator delete【】函数中调用operator delete函数来释放空间。
定位new表达式
定位new表达式是在已分配的原始内存空间中调用构造函数初始化一个对象。
定位new表达式在实际中一般是配合内存池使用。因为内存池分配出的内存没有初始化,所以如果是自定义 类型的对象,需要使用new的定义表达式进行显示调构造函数进行初始化。
示例:
#include<iostream>
using namespace std;class A {
public:A(int n = 1) :_a(n){cout << "A(int n)" << endl;}A(const A& d){_a = d._a;cout << "A(const A& d)" << endl;}~A(){cout << "~A()" << endl;}
private:int _a;
};int main()
{// p1现在指向的只不过是与A对象相同大小的一段空间,还不能算是一个对象,因为构造函数没有执行A* p1 = (A*)malloc(sizeof(A));// 显示调用构造函数new(p1)A(1); // 注意:如果A类的构造函数有参数时,此处需要传参p1->~A();free(p1);return 0;
}
在次场景需要进行显示调用构造函数来进行初始化,与new和delete差不多,都是先申请空间再调用构造。先调用析构函数再使用free释放空间。
总结
malloc、free和new、delete的区别
共同特点是:两者都是在堆上申请空间,并且需要手动释放。
我们从用法、特性和底层来理解和记忆它们的区别:
从用法和特性上:1、malloc和free是函数,而new和delete是操作符。
2、malloc不会初始化,new可以初始化。
3、malloc申请空间需要手动计算类型大小,而new后面只需跟空间的类型就行,在申请多个对象,在【】中指定个数就行。
4、malloc申请的空间类型为void* 需要强转类型,而new后面跟的就是空间的类型。
5、malloc申请空间失败会返回值,因此必须判空,new申请空间失败则会抛异常。
底层上:
对于自定义类型malloc函数申请空间时,不会调用构造和析构函数,而new在申请完空间后会调用对应的构造函数,delete会先调用析构函数清理资源,再释放空间。