【C++初阶】七、内存管理(C/C++内存分布、C++内存管理方式、operator new / delete 函数、定位new表达式)

news2024/11/25 0:34:14

=========================================================================

相关代码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.coloradmin.cn/o/1301638.html

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

相关文章

YOLOv8改进 | 2023检测头篇 | 利用AFPN改进检测头适配YOLOv8版(全网独家创新)

一、本文介绍 本文给大家带来的改进机制是利用今年新推出的AFPN&#xff08;渐近特征金字塔网络&#xff09;来优化检测头&#xff0c;AFPN的核心思想是通过引入一种渐近的特征融合策略&#xff0c;将底层、高层和顶层的特征逐渐整合到目标检测过程中。这种渐近融合方式有助于…

047:vue加载循环倒计时 示例

第047个 查看专栏目录: VUE ------ element UI 专栏目标 在vue和element UI联合技术栈的操控下&#xff0c;本专栏提供行之有效的源代码示例和信息点介绍&#xff0c;做到灵活运用。 &#xff08;1&#xff09;提供vue2的一些基本操作&#xff1a;安装、引用&#xff0c;模板使…

Nature子刊 | snATAC-seq 揭示斑马鱼胚胎早期发育过程中的单核染色质景观

2023年7月19日&#xff0c;一篇题为“Single-nucleus chromatin landscapes during zebrafish early embryogenesis”的研究论文在scientific data&#xff08;IF9.8&#xff09;上发表&#xff0c;该研究使用华大智造单细胞测序平台进行snATAC-seq&#xff0c;建立了斑马鱼胚胎…

openGauss学习笔记-150 openGauss 数据库运维-备份与恢复-物理备份与恢复之gs_backup

文章目录 openGauss学习笔记-150 openGauss 数据库运维-备份与恢复-物理备份与恢复之gs_backup150.1 背景信息150.2 前提条件150.3 语法150.4 参数说明150.5 示例 openGauss学习笔记-150 openGauss 数据库运维-备份与恢复-物理备份与恢复之gs_backup 150.1 背景信息 openGaus…

数据结构之单链表(不带头单向非循环链表)

一.引言 上一节我们学过了顺序表&#xff0c;那么我们想想顺序表有没有问题呢&#xff1f;我们来讨论顺序表的问题及思考。 顺序表问题&#xff1a; 1.中间/头部的插入删除&#xff0c;时间复杂度为O(N) 2. 增容需要申请新空间&#xff0c;拷贝数据&#xff0c;释放旧空间。会…

循环依赖:解析软件设计的迷局

目录 引言 循环依赖的本质 影响与挑战 1. 编译和构建问题 2. 耦合度增加 3. 难以进行单元测试 4. 可扩展性降低 解决循环依赖的策略 1. 模块重构 2. 引入接口抽象 3. 依赖注入 4. 模块化与分层设计 5. 使用工具进行分析 实际案例&#xff1a;Spring框架的循环依赖…

文件系统和磁盘管理应用训练 make编译

一、 掌握Linux下磁盘管理的方法 掌握文件系统的挂载和卸载 掌握磁盘限额与文件权限管理 二、内容&#xff08;详细步骤与结果&#xff09;&#xff1a; &#xff08;1&#xff09;使用 fdisk 命令进行硬盘分区 以 root 用户登录到系统字符界面下输人 fdisk 命令&#xff…

基于ssm保险业务管理系统设计与实现论文

摘 要 现代经济快节奏发展以及不断完善升级的信息化技术&#xff0c;让传统数据信息的管理升级为软件存储&#xff0c;归纳&#xff0c;集中处理数据信息的管理方式。本保险业务管理系统就是在这样的大环境下诞生&#xff0c;其可以帮助管理者在短时间内处理完毕庞大的数据信息…

在普通的项目中创建web的功能

新增web功能: 1.创建一个新项目&#xff0c;不勾选模板&#xff1a;2.添加web功能&#xff1a; 1.创建一个新项目&#xff0c;不勾选模板&#xff1a; 发现普通项目没有webapp文件夹&#xff0c;即没有web的功能。 2.添加web功能&#xff1a; Add framework support:添加一些…

配置本地端口镜像示例(1:1)

本地端口镜像简介 本地端口镜像是指观察端口与监控设备直接相连&#xff0c;观察端口直接将镜像端口复制来的报文转发到与其相连的监控设备进行故障定位和业务监测。 配置注意事项 观察端口专门用于镜像报文的转发&#xff0c;因此不要在上面配置其他业务&#xff0c;防止镜像…

XML映射文件(第二种方式执行SQL语句)

第一种方式是注解的方式在下面&#xff1a; 注解操作SQL语句https://blog.csdn.net/m0_71149935/article/details/134908856?spm1001.2014.3001.5501 要想使用XML&#xff0c;需要遵守三项规范&#xff1a; XML映射文件的名称与Mapper接口名称一致&#xff0c;并且将XML映射…

数据库容灾的设计与实现(五)

六、容灾方案的应用评估 上文中设计了油田数据级容灾系统&#xff0c;完成了基于Oracle Data Guard数据级容灾架构的设计和实施&#xff0c;实现了Broker Failover的FSFO切换技术、触发器提供不间断服务器端服务、客户端使用TAF实现透明故障转移的&#xff0c;完成了数据级容灾…

统信UOS_麒麟KYLINOS上跨架构下载离线软件包

原文链接&#xff1a;统信UOS/麒麟KYLINOS上跨架构下载离线软件包 hello&#xff0c;大家好啊&#xff0c;今天给大家带来一篇在统信UOS/麒麟KYLINOS上跨架构下载离线软件包的实用教程。在我们的日常工作中&#xff0c;可能会遇到这样的情况&#xff1a;需要为不同架构的设备下…

可学习超图拉普拉斯算子代码

python版本&#xff1a;3.6。sklearn版本&#xff1a;scikit-learn0.19 问题1&#xff1a;ERROR: Could not build wheels for ecos, scs, which is required to install pyproject.toml-based projects| 解决办法&#xff1a;cvxpy安装过程中遇到的坑_ecos 2.0.7.post1 cp37 …

使用Python提取PDF文件中指定页面的内容

在日常工作和学习中&#xff0c;我们经常需要从PDF文件中提取特定页面的内容。在本篇文章中&#xff0c;我们将介绍如何使用Python编程语言和两个强大的库——pymupdf和wxPython&#xff0c;来实现这个任务。 1. 准备工作 首先&#xff0c;确保你已经安装了以下两个Python库&…

软件无线电SDR-频谱采集python实现

sdr做的频谱采集&#xff0c;保存的500张频谱图&#xff0c;能看出来是什么东西吗&#xff1f;

SQL错题集2

1.插入记录 用户1001在2021年9月1日晚上10点11分12秒开始作答试卷9001&#xff0c;并在50分钟后提交&#xff0c;得了90分&#xff1b; 用户1002在2021年9月4日上午7点1分2秒开始作答试卷9002&#xff0c;并在10分钟后退出了平台。 2.请把exam_record表中2021年9月1日之前开始作…

2023 CCF中国软件大会(CCF ChinaSoft) “区块链可靠性分析”论坛成功召开

2023年12月1日上午&#xff0c;2023年度CCF中国软件大会区块链可靠性分析论坛成功召开。 本次论坛由中山大学郑子彬、澳门科技大学张涛、中科院软件所蔡彦和中山大学陈嘉弛四位老师联合组织举办。本论坛重点关注区块链可靠性&#xff0c;邀请了近年来在区块链可靠性研究方面有先…

JavaEE 08 线程池简介

前言 前面我们谈完了定时器,单例模式,阻塞队列等的操作并且做了模拟实现,今天我们再来说一说线程池的操作以及一些锁策略. 注:本章几乎均为理论篇,实践较少. 下面就让我们开始吧. 线程池 我们知道因为进程的频繁创建和销毁,带来的开销过大,我们无法接受,所以我们引入了更轻量级…

【rabbitMQ】springboot整合rabbitMQ模拟简单收发消息

目录 1.创建项目和模块 2.添加rabbitMQ依赖 3.启动rabbitMQ服务 4.引入rabbitMQ服务端信息 5.通过单元测试模拟业务发送消息 6. 接收消息 1.创建项目和模块 2.添加rabbitMQ依赖 <!-- rabbitmq依赖--> <dependency> <groupId>org.sp…