【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 mallocdelete 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 <iostream>
#include <assert.h>
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 << "A():" << this << endl;
//	}
//
//	//析构函数:
//	~A()
//	{
//		//调用则打印:
//		cout << "~A():" << this << endl;
//	}
//
//private: //私有成员变量:
//
//	int _a;
//};链表结点类:
//struct ListNode
//{
//	int _val; //结点值
//	ListNode* _next; //next指针
//
//	//构造函数:
//	ListNode(int val)
//		: _val(val)
//		, _next(nullptr)
//	{}
//
//};主函数:
//int main()
//{
//	//C++动态内存管理(对于内置类型):
//	int* p1 = new int; //申请单个空间
//
//	//申请多个空间:
//	int* p2 = new int[10]; //申请10个单位空间
//	//申请40个字节的int数组 -- 后面加:[]
//
//	/*
//	*			使用new申请动态空间(p1、p2):
//	* 对于内置类型 -- 申请后不会对空间进行初始化,会是随机值
//	*(对于 p1 和 p2,单纯就是开空间)
//	*/
//
//	//申请动态空间并初始化单个空间:
//	int* p3 = new int(1); //后面加:(初始化值)
//	//申请一个int类型的空间并初始化为1 -- 后面加:()
//	//注意 new int[10] 和 new int(1) 的区别(易混淆)
//
//	//申请动态空间并初始化多个空间:
//	int* p4 = new int[10] {1,2,3}; 
//	/*
//	* 后面加:[]{}
//	* [初始化空间个数]{第一个初始化值,第二个初始化值,第三个初始化值……}
//	* 
//	* 这里是:[10]{1,2,3} ,即申请了10个单位空间,但只初始化了前3个,
//	* 之后剩余的7个空间会被默认初始化为0,
//	* 即开辟的空间为:{1, 2, 3, 0, 0, 0, 0, 0, 0, 0}
//	*/
//
//	//C++释放new申请的空间 -- delete:
//	delete p1;
//	delete[] p2;
//	delete p3;
//	delete[] p4;
//
//	/*
//	*			总结:
//	* 对于内置类型的对象申请和释放,
//	* 这里 C++的new(delete) 和 
//	* C语言的malloc/calloc/realloc(free)
//	* 除了用法上(“强转”和计算开辟空间大小)外,
//	* (底层)几乎没有任何区别
//	*/
//
//
//
//
//	//对于自定义类型:
//
//	//C语言动态内存管理:
//	A* p5 = (A*)malloc(sizeof(A)); //C语言malloc
//	/*
//	* 这里使用了C语言malloc对自定义类型进行动态空间开辟,
//	* 这里虽然可以开辟,但是无法对其(自定义类型空间)进行初始化,
//	* 因为这里A类中的成员变量是私有的,无法直接调用
//	* 
//	* 所以malloc不方便解决动态申请的自定义类型对象的初始化问题
//	*/
//
//	//C++动态内存管理:
//	A* p6 = new A; //调用默认构造函数(开辟空间后顺便初始化)
//	//new申请动态空间并初始化 -- 自定义类型
//
//	A* p7 = new A(1); //调用有参构造函数(开辟空间后顺便初始化)
//	//new申请动态空间并初始化 -- 自定义类型
//
//	/*
//	* C++中,使用new为自定义类型对象申请空间时,
//	* 除了会开辟动态空间,还会自动调用其构造函数进行初始化,
//	* 
//	* new的本质:开辟动态空间 + 调用构造函数初始化
//	* 
//	* 解决 C语言中开辟空间后无法进行初始化 的问题
//	*/
//
//	//使用new申请一个链表结点:
//	ListNode* n1 = new ListNode(1); //结点1
//	ListNode* n2 = new ListNode(2); //结点2
//	ListNode* n3 = new ListNode(3); //结点3
//	/*
//	*				C++的new带来的便利:
//	* 使用new开辟链表结点,会在开辟后顺便调用其构造函数
//	* 进行结点的初始化,不用像C语言中还需要为了开辟结点空间
//	* 而单独设置一个函数
//	*/
//
//
//	//使用new单词申请多个链表结点:
//
//	//方式一:通过有名对象
//	A aa1(1);
//	A aa2(1);
//	A aa3(1);
//	A* p8 = new A[3]{ aa1, aa2, aa3 };
//
//	//方式二:通过匿名对象
//	A* p9 = new A[3]{ A(2), A(2), A(2) };
//
//	//方式三:将内置类型隐式类型转化为自定义类型
//	A* p10 = new A[3] {3, 3, 3};
//
//	/*
//	* 想要对自定义类型初始化,就需要调用其对应类的构造函数,
//	* 这里要初始化A类型对象,{}大括号中就需要传A类型对象
//	* 
//	* 方式一:A类的有名对象,能找到A类中的构造函数,能初始化
//	* 
//	* 方式二:A类的匿名对象,也能找到A类中的构造函数,能初始化
//	* 
//	* 方式三:
//	* 1、内置类型 -> 构造函数 -> 产生临时对象
//	* 2、临时对象 -> 拷贝构造函数 -> (A类)匿名对象 
//		 -> 找到A类中的构造函数
//	*/
//
//	//释放自定义类型对象空间:
//	delete p6;
//	delete[] p10;
//	/*
//	* delete对于自定义类型:
//	* 先调用析构函数销毁对象清理资源,
//	* 再调用释放动态空间,
//	*/
//
//	return 0;
//}//int main()
//{
//	try
//	{
//		char* p1 = new char[0x7fffffff];
//		/*
//		* 十六进制:0x7fffffff -- 接近2G
//		*
//		* 当new开辟的空间过大时可能会开辟失败,
//		* 开辟失败则会抛出异常
//		*(C语言开辟失败会返回空指针)
//		*/
//		
//		cout << (void*)p1 << endl;
//		/*
//		* char* 在被cout识别时后先被识别为char,
//		* 而不是我们想要打印的指针(地址),
//		* 所以要强转为void*类型
//		*/
//	}
//	catch (const exception& e)
//	{
//		//try……catch……捕获异常:
//		cout << e.what() << endl;
//	}
//
//
//	return 0;
//}栈类:
//class Stack
//{
//public: //公有成员函数:
//
//	//构造函数:
//	Stack(int capacity = 4)
//	{
//		//调用了构造函数则打印:
//		cout << "Stack(int capacity = 4)" << endl;
//
//		//使用new开辟栈容量大小的空间:
//		_a = new int[capacity];
//
//		_top = 0; //栈顶值默认为0
//		_capacity = capacity; //设置栈容量
//	}
//
//	//析构函数:
//	~Stack()
//	{
//		//调用了析构函数则打印:
//		cout << "~Stack()" << endl;
//
//		//使用delete释放new开辟的空间:
//		delete[] _a;
//
//		_a = nullptr; //置为空指针
//		_top = 0; //栈顶值置为0
//		_capacity = 0; //栈容量置为0
//	}
//
//private: //私有成员变量:
//
//	int* _a; //栈指针
//	int _top; //栈顶值
//	int _capacity; //栈容量
//
//};
//
主函数:
//int main()
//{
//	Stack s1;
//	
//	//使用new申请单个栈对象:
//	Stack* p1 = new Stack;
//	//new:开辟空间 + 调用构造函数
//	/*
//	* 这里涉及到两层空间:
//	* 
//	*		栈对象开辟空间:
//	* 先开辟空间,空间大小会自动计算,
//	* 这里栈的三个私有成员变量大小为12个字节,
//	* 此时这12字节大小的空间就是对象,
//	* 此时指针p1就指向这个12个字节的空间
//	*		
//	*	  栈底层数组开辟空间:
//	* 开辟空间后,调用构造函数进行初始化:
//	* _a = new int[capacity];
//	* 构造函数中栈底层数组又需要再new一次,
//	*/
//
//
//	//使用delete释放这个空间:
//	delete p1;
//	//delete:调用析构函数 + 释放空间
//	/*
//	* 这里释放的空间也有两层:
//	* 
//	*		先“销毁”栈底层数组:
//	* delete这里需要先调用栈对象的析构函数,
//	* 来“销毁”栈底层数组(_a指针指向的数组)
//	* 
//	*		再释放整个栈对象:
//	* 再释放整个栈对象。如果先释放栈对象的话,
//	* 栈底层数据指针_a,就会变成野指针了
//	*/
//
//	//operator new 和 operator delete是在库里面的全局函数,
//	//封装了malloc和free:
//	Stack* p2 = (Stack*)operator new(sizeof(Stack));
//	operator delete(p2);
//	/*
//	* operator new / operator delete 和
//	* new / delete 是不一样的,
//	* 但和 malloc / free 是一样的(用法也一样), 
//	* 
//	* new / delete 是操作符,
//	* 而 operator new / operator delete 是函数调用,
//	* new 除了开辟空间还会调用构造函数初始化空间,
//	* operator new 和malloc一样,只会开辟空间不会初始化;
//	* delete 会先调用析构函数清理空间,再释放new开辟的空间,
//	* operator delete 和free一样,只会释放空间不会调用析构函数
//	* 所以 operator new / operator delete 只是 malloc / free 的封装
//	* 
//	* new 实现的两步: 1、开辟对象空间  2、调用构造函数初始化
//	* 其中第一步中,要实现空间开辟可以使用C语言的malloc,
//	* 但是malloc失败只会返回空指针,这不符合面向对象编程的要求,
//	* 所以需要先对malloc进行封装,即 operator new ,
//	* operator new 失败后就可以抛出异常,符合面向对象编程要求,
//	* 所以new关键字的第一步使用的就是malloc封装后operator new,
//	* 如果开辟失败捕获异常,就不会指向第二步的初始化了
//	*(operator new/delete -> 封装malloc/free -> 处理失败抛异常问题)
//	*/
//
//	//上面都是new开辟单个空间,那如果开辟多个空间呢:
//	Stack* p3 = new Stack[10]; //开辟多个空间
//	/*
//	* new 实现的两步: 1、开辟对象空间  2、调用构造函数初始化
//	* 
//	*				1、开辟对象空间
//	* new 开辟单个空间和开辟多个空间同样都会调用operator new,
//	* 开辟多个空间实际只会调用一次operator new,
//	* 一次性就开辟多个连续的空间(这里是10个连续的空间)
//	*(operator new[] -> operator new -> malloc)
//	* 
//	*			 2、调用构造函数初始化
//	* 调用10次Stack构造函数进行初始化
//	*/
//
//	delete[] p3; //释放new开辟的连续空间
//	/*
//	* 1、先调用10次析构函数;
//	* 2、释放空间:
//	* operator delete[] -> operator delete -> free
//	* 
//	*				补充:
//	* 前面我们用new开辟了10个连续的空间,
//	* 按理来说应该是120个字节(这里一个栈对象12个字节),
//	* 但实际开辟了124个字节,多的4个字节存储着开辟的空间个数,
//	* 这里存储就是10,这样第一步中就可以知道要调多少次析构函数,
//	* 第二步中也可以知道释放时要释放多少个连续的空间,
//	* 所以我们使用delete释放连续空间时“delete[]"中的[],
//	* 我们不需要显式写出要释放多少个连续空间,
//	* 因为在用new开辟连续空间的时候就已经存储好了该值
//	*(对于构造函数中申请了资源的自定义类型来说)
//	* 
//	* 所以 new 要和 delete 配对使用,
//	*    new[] 要和 delete[] 配对使用,
//	*   malloc 要和 free 配对使用
//	*/
//
//	return 0;
//}//A类:
class A
{
public: //公有成员函数://构造函数(全缺省):A(int a = 0): _a(a){//调用则打印:cout << "A():" << this << endl;}//析构函数:~A(){//调用则打印:cout << "~A():" << this << endl;}private: //私有成员变量:int _a;
};int main()
{//构造函数只能自动调用:A aa1; //初始化时自动调用//不能显式调用构造函数:A* p1 = (A*)operator new(sizeof(A)); //开辟动态空间//operator new 不会顺便调用构造函数进行初始化//但又不能显式调用构造函数进行初始化:p1->A(1); //不能像437行那样显式调用构造函数,//但可以通过 定位new 显式调用构造函数:new(p1)A(1);/** 定位new是在已分配的原始内存空间中调用构造函数* 来初始化一个对象* *				格式:* 默认构造:new(对象指针)对象类名* 有参构造:new(对象指针)对象类名(初始化值)*///虽然构造函数不能显式调用,但析构函数是可以的:p1->~A();//析构函数可以显式调用也可以自动调用//释放空间:operator delete(p1);/**			某种程度上来说:* *	A* p1 = (A*)operator new(sizeof(A));*				+*		  new(p1)A(1);* * operator new开辟空间配合定位new,可以实现new的功能*(operator new开辟空间,定位new再显式调用构造函数初始化)*//**			某种程度上来说:* *			p1->~A();*				+*		operator delete(p1);* * p1->~A();显式调用析构函数配合operator delete释放空间,* 可以实现delete的功能*//** 虽然可以模拟实现new和delete,* 但一般也不会这么操作*  * 因为new有两步操作,* 1、operator new -> malloc(去堆中申请空间)* 2、调用 构造函数* 所以如果频繁使用new申请小对象的话,一直去找堆的话,* 效率可能会比较低。* * 这时就需要使用到 内存池,* 把堆的内存较大地申请到内存池中,* 这时当需要申请内存时就不到堆中申请了,* 而是到内存池中申请,不够了再到堆中申请,* 这时就不用一直到堆中申请,提高效率* 内存池只开了空间,没有初始化,也不能初始化,* 因为数据可能会是私有的(池化技术)* * 假设我们有一个内存池,在内存池中申请对象空间,* 这时的初始化工作就可以交给 定位new ,* 通过 定位new 显式调用构造函数来初始化对象,* 这时要释放空间还给内存池的话,* 就需要显式调用析构函数来释放空间*/return 0;
}//C++常见面试题:指针和引用的区别、malloc和new的区别

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/213471.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

16.Java程序设计-基于SSM框架的android餐厅在线点单系统App设计与实现

摘要&#xff1a; 本研究旨在设计并实现一款基于SSM框架的Android餐厅在线点单系统&#xff0c;致力于提升餐厅点餐流程的效率和用户体验。通过整合Android移动应用和SSM框架的优势&#xff0c;该系统涵盖了用户管理、菜单浏览与点单、订单管理、支付与结算等多个功能模块&…

用户登录权限

文章目录 [TOC](文章目录) 前言一、 Cookie与session1.HTTP无状态2.cookie 和 session 的生命周期2.1 cookie 生命周期影响因素2.2 session 生命周期影响因素 3.cookie 和 session 的区别4.工作原理3 用户登录Node.js和Express验证session 二、JSON Web Token1. JWT 介绍2. JWT…

【算法系列篇】递归、搜索和回溯(二)

文章目录 前言1. 两两交换链表中的节点1.1 题目要求1.2 做题思路1.3 代码实现 2. Pow(X,N)2.1 题目要求2.2 做题思路2.3 代码实现 3. 计算布尔二叉树的值3.1 题目要求3.2 做题思路3.3 代码实现 4. 求根节点到叶结点数字之和4.1 题目要求4.2 做题思路4.3 代码实现 前言 前面为大…

计算机毕业设计springboot+ssm停车场车位预约系统java

管理员不可以注册账号 停车位包括车位所在楼层、车位编号、车位类型(全时间开放/高峰期开放)、预定状态等 用户预约时要求支付预约时间段的停车费用 违规行为&#xff1a;1.停车超过预约时间段 2.预约未使用 于系统的基本要求 &#xff08;1&#xff09;功能要求&am…

6G来袭,真的有必要吗?

6G来袭&#xff0c;6G标准将在2025年完成制定&#xff0c;2030年商用。当5G都还没玩明白的时候&#xff0c;6G又来了。 这次6G又提出了三个全新高大上场景&#xff0c;感知通信、人工智能通信、天地一体泛在物联&#xff0c;精英们还说&#xff0c;未来要连接很多机器人、元宇宙…

PHP基础 - 循环与条件语句

循环语句 1)for循环: 重复执行一个代码块指定的次数。 for ($i = 0; $i < 5; $i++) { // 初始化 $i 为 0,每次循环后将 $i 值增加 1,当 $i 小于 5 时执行循环echo "The number is: $i \n"; // 输出当前 $i 的值并换行 }// 循环输出结果为: // The number …

mysql字段设计规范:使用unsigned(无符号的)存储非负值

如果一个字段存储的是数值&#xff0c;并且是非负数&#xff0c;要设置为unsigned&#xff08;无符号的&#xff09;。 例如&#xff1a; 备注&#xff1a;对于类型是 FLOAT、 DOUBLE和 DECIMAL的&#xff0c;UNSIGNED属性已经废弃了&#xff0c;可能在mysql的未来某个版本去…

C++ //习题2.5 请写出下列表达式的值。

C程序设计 &#xff08;第三版&#xff09; 谭浩强 习题2.5 习题2.5 请写出下列表达式的值。 (1) 3.5 * 3 2 * 7 - ‘a’ (2) 26 / 3 34 % 3 2.5 (3) 45 / 2 (int)3.14159 / 2 (4) a b (c a 6) 设a的初值为3 (5) a 3 * 5, a b 3 * 2 (6) (int)(a 6.5) % 2 …

UI自动化测试工具的定义及重要性

UI自动化测试工具在现代软件开发中起着不可或缺的作用。它们能够提高测试效率、减少人为错误、提供全面的测试覆盖&#xff0c;并支持持续集成。通过有效使用UI自动化测试工具&#xff0c;开发团队可以提高软件质量&#xff0c;提供更可靠的应用程序&#xff0c;满足用户的需求…

C语言之数组精讲(2)

目录 数组的复制 输入数组元素的值 对数组的元素进行倒序排列 使用数组进行成绩处理 对象式宏 数组元素的最大值和最小值 赋值表达式的判断 数组的元素个数 结语 数组的复制 我们把数组中的元素全部复制到另一个数组中。 #include<stdio.h>int main() {int i;int…

SwinIR: Image Restoration Using Swin Transformer

SwinIR 简介 论文地址&#xff1a;SwinIR: Image Restoration Using Swin Transformer 代码&#xff1a;SwinIR ​ 本文提出了一个基于swin transformer的图像超分模型swinIR。其中SwinIR分为三部分&#xff1a;浅层特征提取、深层特征提取和高质量图像重建模块。 现阶段问…

19.java程序设计-基于SpringBoot的博客管理系统的设计与实现

摘要 随着信息技术的迅速发展&#xff0c;博客作为一种重要的信息传播和交流工具&#xff0c;逐渐在互联网上占据重要地位。为了满足用户对个性化博客管理的需求&#xff0c;本研究设计并实现了一种基于Spring Boot框架的博客管理系统。 本系统通过采用前后端分离的架构&…

【算法题】密钥格式化 (js)

!](https://img-blog.csdnimg.cn/direct/bf9a3d781a8043c997593260c0a8306f.png) 第一部分的字符可以少于… const str "5F3Z-2e-9w"; const str1 "2-5g-3-J"; function solution(num, str) {const arr str.split("-");const head arr[0];…

【C++11(三)】智能指针详解--RAII思想循环引用问题

&#x1f493;博主CSDN主页:杭电码农-NEO&#x1f493;   ⏩专栏分类:C从入门到精通⏪   &#x1f69a;代码仓库:NEO的学习日记&#x1f69a;   &#x1f339;关注我&#x1faf5;带你学习C   &#x1f51d;&#x1f51d; C11 1. 前言2. 为什么要有智能指针?3. RAII思想…

【BUG】微信小程序image不会随着url动态变化

问题描述&#xff1a; 第一次打开界面&#xff0c;显示的是默认头像而不是用户头像&#xff0c;似乎image里面的src只要第一次有值就不会再更新了 解决 不要给src里面的变量设置初始值&#xff0c;而是直接赋空值

DevOps搭建(七)-安装Jenkins详细步骤

这里我们用Docker进行安装 1、拉取Jenkins镜像 Jenkins download and deployment 选择LTS长期支持的版本,接着点击Docker链接进入 找到上面的版本,并copy拉取镜像的命令 docker pull jenkins/jenkins:2.426.1-lts 2、docker-compose安装Jenkins 首先创建安装目录/home/f…

STM32 cubeMX 呼吸灯实验

文章代码使用 HAL 库。 文章目录 一、1.PWM原理二、LED 原理图三、使用cubemx 配置 led四、PWM 相关函数五、PWM占空比占空比计算六、PWM 呼吸灯重要代码总结 呼吸灯 一、1.PWM原理 PWM全称为脉冲宽度调制&#xff08;Pulse Width Modulation&#xff09;&#xff0c;是一种常…

拥有大量虾皮买家号有哪些好处

拥有众多Shopee买家账号&#xff0c;无疑是卖家们获取极大优势的一项策略。多账号的运用不仅有助于卖家在Shopee平台上获得更为丰富的流量&#xff0c;更能够在关键词排名和销售表现等方面为其带来显著提升。 首先&#xff0c;多个Shopee买家账号的灵活运用&#xff0c;使卖家能…

JavaScript <有道翻译之数据解密‘23年12月06日版‘>--案例(三)

前言: 记得上半年还是去年,有道翻译还是直接返回明文数据;现在也跟着,用接口返回加密数据了; 娱乐一下,破他的密文数据... 成品效果图: js部分: 对于找他的密文数据有点费时,针对密文--->搜他地址和启动器不是特别容易,辗转多时(搜:descrypt/json.parse 结合使用更快),有图…

swing快速入门(四)

注释很详细&#xff0c;直接上代码 上一篇 增加内容 流式布局范例 import java.awt.*;public class swing_test_2{public static void main(String[] args){//创建一个窗口对象Frame framenew Frame("test");//设置窗口大小frame.setSize(800,800);//这里演示的是…