前言
本篇文章我们一起来学习C++的内存管理方式,实际上C++与C语言的内存管理模式是十分相似的,他们的内存分布完全一致,C语言所学习的内存管理函数在C++中仍然适用,而new与delete的产生主观上认为是为了解决自定义类型的内存管理。
欢迎大家📂收藏📂以便未来做题时可以快速找到思路,巧妙的方法可以事半功倍。
=========================================================================
GITEE相关代码:🌟fanfei_c的仓库🌟
=========================================================================
1.C/C++内存分布
复习下C语言的内容:
int globalVar = 1; static int staticGlobalVar = 1; void Test() { static int staticVar = 1; int localVar = 1; int num1[10] = { 1, 2, 3, 4 }; char char2[] = "abcd"; const char* pChar3 = "abcd"; int* ptr1 = (int*)malloc(sizeof(int) * 4); int* ptr2 = (int*)calloc(4, sizeof(int)); int* ptr3 = (int*)realloc(ptr2, sizeof(int) * 4); free(ptr1); free(ptr3); } //1. 选择题: //选项 : A.栈 B.堆 C.数据段(静态区) D.代码段(常量区) //globalVar在哪里?____ staticGlobalVar在哪里?____ //staticVar在哪里?____ localVar在哪里?____ //num1 在哪里?____ //char2在哪里?____ * char2在哪里?___ //pChar3在哪里?____ * pChar3在哪里?____ //ptr1在哪里?____ * ptr1在哪里?____ //2. 填空题: //sizeof(num1) = ____; //sizeof(char2) = ____; strlen(char2) = ____; //sizeof(pChar3) = ____; strlen(pChar3) = ____; //sizeof(ptr1) = ____; //3. sizeof 和 strlen 区别?
答案:
1. 选择题:
选项: A.栈 B.堆 C.数据段(静态区) D.代码段(常量区)
globalVar在哪里?__C__ staticGlobalVar在哪里?__C__
staticVar在哪里?__C__ localVar在哪里?__A__
num1 在哪里?__A__
分析:
globalVar全局变量在数据段 staticGlobalVar静态全局变量在静态区
staticVar静态局部变量在静态区 localVar局部变量在栈区
num1局部变量在栈区
char2在哪里?__A__ *char2在哪里?__A__
pChar3在哪里?__A__ *pChar3在哪里?__D__
ptr1在哪里?__A__ *ptr1在哪里?__B__
分析:
char2局部变量在栈区
char2是一个数组,把后面常量串拷贝过来到数组中,数组在栈上,所以*char2在栈上
pChar3局部变量在栈区 *pChar3得到的是字符串常量字符在代码段
ptr1局部变量在栈区 *ptr1得到的是动态申请空间的数据在堆区
2. 填空题:
sizeof(num1) = __40__;//数组大小,10个整形数据一共40字节
sizeof(char2) = __5__;//包括\0的空间
strlen(char2) = __4__;//不包括\0的长度
sizeof(pChar3) = __4__;//pChar3为指针
strlen(pChar3) = __4__;//字符串“abcd”的长度,不包括\0的长度
sizeof(ptr1) = __4__;//ptr1是指针
全局变量和在全局定义的静态变量的区别:
- 最主要的区别在于链接属性不同,static修饰的全局变量只在当前文件可用,而全局变量其他文件也可以使用。
说明:
- 栈又叫堆栈--非静态局部变量/函数参数/返回值等等,栈是向下增长的,栈空间由系统分配,但可以通过函数alloca进行动态分配,不过注意,所分配空间不能通过free或delete进行释放。
- 内存映射段是高效的I/O映射方式,用于装载一个共享的动态内存库。用户可使用系统接口创建共享共享内存,做进程间通信。(了解)
- 堆用于程序运行时动态内存分配,堆是可以上增长的,堆只能动态分配,不能静态分配。
- 数据段--存储全局数据和静态数据。
- 代码段--可执行的代码/只读常量
🔎做几道小题🔍
分析:
A.栈区主要存在局部变量和函数参数,其空间的管理由编译器自动完成,无需手动控制,堆区是自己申请的空间,在不需 要时需要手动释放
B.栈区先定义的变量放到栈底,地址高,后定义的变量放到栈顶,地址低,因此是向下生长的,堆区则相反
C.频繁的申请空间和释放空间,容易造成内存碎片,甚至内存泄漏,栈区由于是自动管理,不存在此问题
D.32位系统下,最大的访问内存空间为4G,所以不可能把所有的内存空间当做堆内存使用,故错误
答案:D
分析:
A.堆大小受限于操作系统,而栈空间一般有系统直接分配
B.频繁的申请空间和释放空间,容易造成内存碎片,甚至内存泄漏,栈区由于是自动管理,不存在此问题
C.堆无法静态分配,只能动态分配
D.栈可以通过函数alloca进行动态分配,不过注意,所分配空间不能通过free或delete进行释放
答案:C
2.C++内存管理方式
C++为了解决自定义类型的内存管理,提出了自己的内存管理方式:通过new和delete操作符进行动态内存管理。
2.1new/delete操作内置类型
void Test() { // 动态申请一个int类型的空间 int* ptr4 = new int; // 动态申请一个int类型的空间并初始化为10 int* ptr5 = new int(10); // 动态申请10个int类型的空间 int* ptr6 = new int[3];//int* ptr6 = new int[3]{1,2,3}初始化 delete ptr4; delete ptr5; delete[] ptr6; }
注意:申请和释放单个元素的空间,使用new和delete操作符,申请和释放连续的空间,使用
new[]和delete[],注意:匹配起来使用
2.2new/delete操作自定义类型
class A { public: A(int a = 0) : _a(a) { cout << "A():" << this << endl; } ~A() { cout << "~A():" << this << endl; } private: int _a; }; int main() { A* p1 = (A*)malloc(sizeof(A)); A* p2 = new A(1); free(p1); delete p2; // 内置类型是几乎是一样的 int* p3 = (int*)malloc(sizeof(int)); // C int* p4 = new int; free(p3); delete p4; A* p5 = (A*)malloc(sizeof(A) * 10); A* p6 = new A[10]; free(p5); delete[] p6; return 0; }
new/delete 和 malloc/free最大区别:
new/delete对于【自定义类型】除了开空间,还会调用构造函数和析构函数.
3.operator new与operator delete函数
operator new与operator delete是系统提供的两个全局函数,本质上可以等价为malloc和free,他们不会调用构造函数与析构函数.
那C++为什么会存在这两个全局函数,他们的意义是什么呢?
我们知道new操作符可以干两件事:
1.开空间
2.调用构造函数
那开空间是怎么开的呢?
实际上就利用了operator new这个全局函数.
可是本来不是有malloc,为啥又单独弄一个operator new呢?
我们知道C++是面向对象的语言,而malloc如果申请失败的话返回的是0,可是面向对象的语言可不认这个0,面向对象语言对于失败的处理一般都是抛异常,也就是说operator new就是malloc的封装,只不过多实现了抛异常这个过程罢了.
那delete在底层释放空间时也是会调用operator delete,其余的细节和上面相似就不过多赘述了.
另外对于new T[n]和delete[],C++也提供了他们对应开空间的全局函数,分别为operator new[]与operator delete[],实际上这两个全局函数的内部还是operator new与operator delete,大家可以理解为换皮(仅为了区分用).
🐸总结一下🐸
new的原理
1. 调用operator new函数申请空间
2. 在申请的空间上执行构造函数,完成对象的构造
delete的原理
1. 在空间上执行析构函数,完成对象中资源的清理工作
2. 调用operator delete函数释放对象的空间
new T[N]的原理
1. 调用operator new[]函数,在operator new[]中实际调用operator new函数完成N个对
象空间的申请
2. 在申请的空间上执行N次构造函数
delete[]的原理
1. 在释放的对象空间上执行N次析构函数,完成N个对象中资源的清理
2. 调用operator delete[]释放空间,实际在operator delete[]中调用operator delete来释
放空间
4.高频面试题
👓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在释放空间前会调用析构函数完成空间中资源的清理。
=========================================================================
如果你对该系列文章有兴趣的话,欢迎持续关注博主动态,博主会持续输出优质内容
🍎博主很需要大家的支持,你的支持是我创作的不竭动力🍎
🌟~ 点赞收藏+关注 ~🌟
=========================================================================