【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…

C#使用Matrix类对Dicom图像的放缩

C#使用Matrix类对Dicom图像的放缩&#xff0c;使用Matrix 1.同时操作水平、垂直同时放缩 // 创建一个 Matrix 对象 Matrix m_Matrix new Matrix();//放缩参数 float inputZoom1.2f; m_Matrix.Scale(inputZoom, inputZoom, MatrixOrder.Append); 2.操作水平&#xff08;X轴…

前端使用插件预览pdf、docx、xlsx、pptx格式文件

PDF预览 H5页面pdf预览 插件&#xff1a;pdfh5 版本&#xff1a;“pdfh5”: “^1.4.7” npm install pdfh5 import PdfH5 from "pdfh5"; import "pdfh5/css/pdfh5.css";// methods this.$nextTick(() > {this.pdfH5 new PdfH5("#pdf", {pd…

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

文章目录 前言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的未来某个版本去…

mysql分别在windows和linux下的备份策略

嗟乎&#xff01; 一、概述 mysql数据库该怎么备份呢&#xff1f; 数据库备份有几个概念&#xff1a;全量备份、增量备份、差异备份。当然啦&#xff0c;数据库备份又有冷备份和热备份&#xff0c;即物理备份和逻辑备份之分。冷备份就是将mysql停了&#xff0c;然后直接拷贝…

Python入门第2篇

pip包管理器 包管理器类似.NET下的nuget&#xff0c;主要用于管理引用依赖项。 安装Python的时候&#xff0c;已经默认安装了pip包管理器&#xff0c;因此无需单独安装 cmd&#xff0c;输入&#xff1a;pip --version 显示pip版本号信息&#xff0c;即代表pip安装成功&…

前端知识笔记(四十二)———http和https详细解析

HTTP&#xff08;Hypertext Transfer Protocol&#xff09;是一种用于在计算机网络中传输超文本的协议。它是一个客户端-服务器协议&#xff0c;用于从 Web 服务器传输超文本到本地浏览器。HTTP 使用 TCP/IP 协议作为底层传输协议&#xff0c;并使用默认端口号80。 HTTPS&…

8-tornado中模板的使用(通过字符串返回、通过模板Template返回、通过模板render返回)、模板案例

1 Template 1.1 通过字符串返回 import tornado class IndexHandler(web.RequestHandler):def get(self):arg Templateself.finish(f<h1>Hello {arg}!!</h1>)1.2 通过模板Template返回 tornado.template 一个简单的模板系统&#xff0c;将模板编译为Python代码。…

c 一,二,三维数组的定义和赋值

1. 定义数组必须指定数组的大小&#xff0c;也就是用多少存储空间来存储此数组 2.定义数组必须用数组的标准格式定义&#xff1a;数组名下标的形式 3.只有字符串可以用指针来定义 4.可以把c 中一切数和struct 理解为char 数组 比如int 就是4字节的char数组 #include <…

编程语言的演进历程与未来发展趋势

第一代 编程语言的发展历程起源于早期的机器语言阶段&#xff0c;这是一种由二进制代码构成的计算机能够直接解读并执行的语言。然而&#xff0c;鉴于其过于复杂且难以理解&#xff0c;故这一时代的语言并不常为人类所采纳。 第二代 紧接着产生的第二代语言旨在简化编程过程…

1001 害死人不偿命的(3n+1)猜想

卡拉兹(Callatz)猜想&#xff1a; 对任何一个正整数 n&#xff0c;如果它是偶数&#xff0c;那么把它砍掉一半&#xff1b;如果它是奇数&#xff0c;那么把 (3n1) 砍掉一半。这样一直反复砍下去&#xff0c;最后一定在某一步得到 n1。卡拉兹在 1950 年的世界数学家大会上公布了…

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;浅层特征提取、深层特征提取和高质量图像重建模块。 现阶段问…