【C++初阶】七、内存管理(C/C++内存分布、C++内存管理方式、operator new / delete 函数、定位new表达式)
=========================================================================
相关代码gitee自取:
C语言学习日记: 加油努力 (gitee.com)
=========================================================================
接上期:
【C++初阶】六、类和对象(初始化列表、static成员、友元、内部类)-CSDN博客
=========================================================================
目录
一 . C/C++内存分布
C/C++中程序内存区域划分:
二 . C++内存管理方式
回顾:C语言中动态内存管理方式malloc / calloc / realloc / free
C++的内存管理方式
new / delete -- 操作内置类型:
new / delete -- 操作自定义类型:
常见面试题 -- malloc / free 和 new / delete 的区别
三 . operator new 和 operator delete 函数
operator new / operator delete
operator new 全局函数:
operator delete 全局函数:
图示 -- operator new / delete 全局函数:
new 和 delete 的实现原理
对于内置类型:
(重点)对于自定义类型:
四 . 定位new表达式(placement-new)(了解)
本篇博客相关代码
Test.cpp文件 -- C++文件:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
一 . C/C++内存分布
C/C++中程序内存区域划分:
不同的数据有不同的存储需求,内存中有各种区域满足不同的需求
- 栈(堆栈):
存放非静态局部变量 / 函数参数 / 返回值 ……,栈是向下增长的 - 内存映射段:
内存映射段是最高效的 I/O映射方式 ,用于装载一个共享的动态内存库。
用户可以使用系统接口创建共享内存,做进程间通信 - 堆:
用于程序运行时动态内存分配,堆是向上增长的(动态使用:数据结构、算法中需要动态开辟一些空间)
- 数据段(静态区):
操作系统角度叫数据段,语言角度叫静态区。
存储全局数据和静态数据(整个程序运行期间都可能会使用到的数据)
- 代码段(常量区):
操作系统角度叫代码段,语言角度叫常量区。
存储可执行代码(汇编指令)和常量(只读数据)
图示:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
二 . C++内存管理方式
回顾:
C语言中动态内存管理方式malloc / calloc / realloc / free
之前学习C语言的时候有写过动态内存管理相关内容,
有需要的话可以进行查看:学C的第三十二天【动态内存管理】_高高的胖子的博客-CSDN博客
C++的内存管理方式
C语言内存管理方式在C++中可以继续使用,但有些地方就无能为力了,
而且使用起来会比较麻烦,因此C++中又提出了自己的内存管理方式:
通过 new 和 delete 操作符进行动态内存管理
new / delete -- 操作内置类型:
- new -- 申请单个空间:
内置类型指针 指针名 = new 内置类型;
- new -- 申请多个空间:
内置类型指针 指针名 = new 内置类型[申请单位空间个数];
- new -- 申请单个空间并进行初始化:
内置类型指针 指针名 = new 内置类型(初始化值);
- new -- 申请多个空间并进行初始化:
内置类型指针 指针名 = new 内置类型[申请单位空间个数]{第一个初始化值, 第二个初始化值……};
- delete -- 释放new申请的空间:
//释放new申请的单个空间: delete 内置类型指针名; //释放new申请的多个空间: delete[] 内置类型指针名;
- 对于内置类型的对象申请和释放,
C++的 new / delete 和 C语言的 malloc / calloc / realloc / free
除了用法上(“强转”和计算开辟空间大小)外,(底层)几乎没有任何区别图示:
---------------------------------------------------------------------------------------------
new / delete -- 操作自定义类型:- new -- 申请单个空间:
对于自定义类型,使用C++中的new开辟动态空间的话,
会在开辟空间后顺便调用其构造函数进行自定义类型对象的初始化//开辟单个空间并自动调用 默认构造函数 进行初始化: 自定义类型指针 指针名 = new 自定义类型; //开辟单个空间并调用 有参构造函数 进行初始化: 自定义类型指针 指针名 = new 自定义类型(初始化值);
- new -- 申请多个空间:
对于自定义类型,使用new申请多个空间时,
同样会在开辟空间后顺便调用其构造函数进行自定义类型对象的初始化//方式一:通过有名对象: (先初始化多个自定义类型对象); 自定义类型指针 指针名 = new 自定义类型[申请单位空间个数]{有名对象1, 有名对象2……}; //方式二:通过匿名对象: 自定义类型指针 指针名 = new 自定义类型[申请单位空间个数]{匿名对象1, 匿名对象2……}; //方式三:通过内置类型的隐式类型转换为自定义类型: 自定义类型指针 指针名 = new 自定义类型[申请单位空间个数]{内置类型1, 内置类型2……};
- delete -- 释放new申请的空间:
//释放new申请的单个空间: delete 自定义类型指针名; //释放new申请的多个空间: delete[] 自定义类型指针名;
- 对于自定义类型的对象申请和释放,
C++的 new 除了会开辟动态空间外,还会自动调用其构造函数进行初始化图示:
---------------------------------------------------------------------------------------------
常见面试题 -- malloc / free 和 new / delete 的区别
共同点:
malloc / free 和 new / delete 都是从堆上申请空间的,并且都需要用户手动释放
---------------------------------------------------------------------------------------------
不同点:
- malloc 和 free 是函数 ;new 和 delete 是操作符
- malloc 申请的空间不会被初始化 ;new 申请的空间则会被初始化
- malloc 申请空间时,需要手动计算开辟的空间大小并传递;
new 申请空间时,只需要在其后写出空间的类型即可,
如果是多个对象,[ ]中指定对象个数即可 - malloc 的返回值为 void* ,在使用时必须进行强转;
new 则不需要,因为 new 后跟的是空间的类型 - malloc 申请空间失败时,返回的是空指针NULL,因此使用时必须判空;
new 则不需要,但是 new 需要捕获异常 - 在申请自定义类型对象时:
malloc / free 只会开辟空间,不会调用构造函数和析构函数;
new 在申请空间后会调用构造函数完成对象的初始化,
delete 在释放空间前会调用析构函数完成空间中资源的清理~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
三 . operator new 和 operator delete 函数
operator new / operator delete
new 和 delete 是C++中进行动态内存申请和释放的操作符,
operator new 和 operator delete 是系统提供的全局函数,
new 在底层会调用 operator new 全局函数来申请空间;
delete 在底层会调用 operator delete 全局函数来释放空间。
operator new 全局函数:
- 虽然函数名中有 operator ,但并不是重载函数
- C语言中,malloc 如果申请空间失败的话,会返回空指针,
这不符合C++面向对象编程的要求,所以需要对其进行封装 - operator new 全局函数就是对 malloc 的封装,
所以 operator new 全局函数底层会调用 malloc ,
让 malloc 申请空间失败后会抛出异常,从而能够符合C++面向对象编程的要求,
operator new 全局函数和 malloc 一样只会申请空间不会调用构造函数初始化---------------------------------------------------------------------------------------------
operator delete 全局函数:
- operator delete 全局函数同样也不是重载函数,而是一个全局函数
- operator delete 全局函数是对 free 的封装,
所以 operator delete 全局函数底层会调用 free ,
相较 free ,operator delete 全局函数多了一些检查,
operator delete 全局函数和 free 一样只会释放空间不会调用析构函数---------------------------------------------------------------------------------------------
图示 -- operator new / delete 全局函数:
new 和 delete 的实现原理
对于内置类型:
如果申请的是内置类型对象的空间,new 和 malloc,delete 和 free 基本类似,
不同的地方是:new / delete 申请和释放的是单个元素的空间;new[ ] / delete[ ] 操作的则是连续的空间,
而且 new 在申请空间失败时会抛出异常,而C语言中malloc则会返回空指针
---------------------------------------------------------------------------------------------
(重点)对于自定义类型:
- new 的原理(申请单个动态空间):
第一步 -- 为自定义类型对象开辟动态空间 -- 调用 operator new 全局函数(new => operator new => malloc)
第二步 -- 初始化申请的空间 -- 调用 构造函数 完成对象的初始化 - delete 的原理(释放单个动态空间):
第一步 -- 先清理自定义类型对象申请的资源 -- 调用对应的 析构函数
第二步 -- 再释放自定义类型对象的动态空间 -- 调用 operator delete 全局函数(delete => operator delete => free)
- new T[N] 的原理(申请多个动态空间):
第一步 -- 调用 operator new[ ] 函数开辟动态空间,
在 operator new[ ] 中实际也是调用了 operator new 全局函数,
一次性完成了N个对象空间的申请
第二步 -- 在申请的空间上执行N次构造函数,完成N个对象的初始化 - delete[ ] 的原理(释放多个动态空间):
第一步 -- 在释放的对象空间上执行N次析构函数,完成N个对象中资源的清理
第二步 -- 调用 operator delete[ ] 释放空间,
在 operator delete[ ] 中实际也是调用了 operator delete 全局函数图示:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
四 . 定位new表达式(placement-new)(了解)
- 定位new表达式是在已分配的原始内存空间中调用构造函数来初始化一个对象
(通过对象指针能够显式调用构造函数进行初始化)
- 使用格式:
调用默认构造函数 -- new (place_address) type
调用有参构造函数 -- new (place_address) type (initializer-list)
place_address:必须是一个指针 ;initializer-list:类型的初始化列表 - 使用场景:
定位new表达式在实际中一般是配合内存池进行使用。
因为内存池分配出的内存没有被初始化,所以如果是自定义类型的对象,
则需要使用new的定位表达式进行显式调用构造函数来进行初始化图示:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
本篇博客相关代码
Test.cpp文件 -- C++文件:
#define _CRT_SECURE_NO_WARNINGS 1 #include #include using namespace std; 全局变量(链接属性:其它文件也可用): //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"; // /* // * 这里没有使用数组符号[]进行拷贝, // * 所以指针是直接指向常量区中“abcd”的位置的 // */ // // //malloc开辟动态空间: // int* ptr1 = (int*)malloc(sizeof(int) * 4); // //calloc开辟动态空间: // int* ptr2 = (int*)calloc(4, sizeof(int)); // //realloc开辟动态空间: // int* ptr3 = (int*)realloc(ptr2, sizeof(int) * 4); //} A类: //class A //{ //public: //公有成员函数: // // //构造函数(全缺省): // A(int a = 0) // : _a(a) // { // //调用则打印: // cout
- 定位new表达式是在已分配的原始内存空间中调用构造函数来初始化一个对象
- new 的原理(申请单个动态空间):
- operator delete 全局函数同样也不是重载函数,而是一个全局函数
- 虽然函数名中有 operator ,但并不是重载函数
- malloc 和 free 是函数 ;new 和 delete 是操作符
- new -- 申请单个空间:
- new -- 申请单个空间: