C++ operator new和operator delete的深入讲解

 个人主页:Jason_from_China-优快云博客

所属栏目:C++系统性学习_Jason_from_China的博客-优快云博客

所属栏目:C++知识点的补充_Jason_from_China的博客-优快云博客

前言

关于operator new和operator delete我们需要明确一个概念,这两个都是一个函数,和malloc,free一样都是一个函数,但是这里需要明确的是,这里只是类似,不是一样。

operator new语法结构

语法结构

#include<iostream>
int main()
{
	//标准的分配内存的空间形式
	//分配一个内置类型,int内置类型的空间
	void* ptr1 = operator new(sizeof(int));
	//分配一个数组形式的内存空间
	void* ptr4 = operator new[](10 * sizeof(int));

	//这样的形式也是可以使用的,但是可能会出现问题,因为operator new是一个没有初始化,也就是未定义的内存空间
	//这样分配内存容易导致错误访问,所以还是建议使用标准化来分配内存空间
	//void* ptr2 = operator new(10 * sizeof(int));
	//void* ptr3 = operator new[](sizeof(int));
	//void* ptr4 = operator new[](10 * sizeof(int));

	return 0;
}

operator delete语法结构

#include<iostream>
int main()
{
	//标准的分配内存的空间形式
	//分配一个内置类型,int内置类型的空间
	void* ptr1 = operator new(sizeof(int));
	//分配一个数组形式的内存空间
	void* ptr4 = operator new[](10 * sizeof(int));

	//这样的形式也是可以使用的,但是可能会出现问题,因为operator new是一个没有初始化,也就是未定义的内存空间
	//这样分配内存容易导致错误访问,所以还是建议使用标准化来分配内存空间
	//void* ptr2 = operator new(10 * sizeof(int));
	//void* ptr3 = operator new[](sizeof(int));
	//void* ptr4 = operator new[](10 * sizeof(int));

	//operator new和delete这里是函数,所欲我们销毁的时候是函数的形式销毁,销毁的语法结构
	//释放单个内存空间
	

	//释放内存空间、标准化释放内存空间
	operator delete(ptr1);
	operator delete[](ptr4);


	return 0;
}

operator new+operator delete原理讲解

关于operator new

  • operator new我们可以看出来,其实new是operator new的一个封装,因为new在使用的时候会调用operator new
  • operator new的底层实现上面是调用malloc来实现开辟空间的


 

关于operator delete

  • 从operator delete我们可以看出来,delect本质也是对operator delete函数的封装,再严谨的讲解就是,是对free的封装,free是对free_dbg(p,_NORMAL_BLOCK)的封装
  • 所以我们可以更清晰的看出,operator delete是一个函数,不是关键字
  • delete是关键字,不是函数

operator new+operator delete和new+delete的深入对比

一、内置类型

  1. 对于内置类型,new 和 malloc、delete 和 free 基本类似。不同在于:new/delete 申请和释放单个元素空间,new []/delete [] 申请和释放连续空间;new 申请空间失败会抛异常,malloc 失败返回 NULL。
  2. 抛异常(就是告诉你哪里有错,并且继续运行程序)

二、自定义类型 new 的原理

  1. 调用 operator new 函数申请空间,底层类似 malloc(malloc 不抛异常)。
  2. 在申请的空间上执行构造函数完成对象构造。

三、自定义类型 delete 的原理

  1. 在空间上执行析构函数清理对象资源,本质类似 free 的调用。
  2. 调用 operator delete 函数释放对象空间。

四、new T [N] 的原理

  1. 调用 operator new [] 函数实际在其中调用 operator new 完成 N 个对象空间申请。
  2. 在申请的空间上执行 N 次构造函数。

五、delete [] 的原理

 
  1. 在释放的对象空间上执行 N 次析构函数,清理 N 个对象资源。
  2. 调用 operator delete [] 释放空间,实际在其中调用 operator delete。

operator new+operator delete和new+delete使用时候的注意事项

不要交错使用,很容易导致资源使用出现问题

  • operator new只是开辟空间,不会进行初始化的
  • operator delete是只是销毁空间,不会清理资源的
  • new,在开辟空间的时候会初始化并且构建资源
  • delete,销毁空间的时候会调用构造函数销毁资源
  • 知道,尽量不要交错使用就可以
#include<iostream>
using namespace std;

class A
{
public:
	A(int capacity = 4, int size = 0)
		:_Capacity(capacity)
		, _size(size)
		, _arr(nullptr)
	{
		//创建空间
		_arr = new int[_Capacity];
		printf("A()");
	}
	~A() 
	{
		//这里释放我们是需要匹配方括号,这里是释放数组形式的内容,自适应找到需要释放的内存
		delete[] _arr;
		_Capacity = 4;
		_size = 0;
		printf("~A()");

	}

private:
	int* _arr;
	int _Capacity;
	int _size;
};




//operator new创建空间,new构造
//我们需要使用 operator delete先销毁空间,再使用delete销毁资源
//并且是不能直接使用delete来进行销毁空间的,因为我们创建的空间是未定义的,new构造之后我们是会申请资源甚至空间的
//如果直接用delect销毁不使用operator delete销毁就会导致空间没有销毁
//如果只是使用operator delete销毁空间,就会导致资源没有销毁
//并且此时还应该先试用delete来销毁资源 再销毁空间
int main()
{
	A* p = new A[10];
	delete[] p;
	printf("\n");
	void* ptr1 = operator new[](10 * sizeof(A));
	//初始化,我们也可以进行定位new进行初始化,定位new里面我们会进行讲解
	for (int i = 0; i < 10; ++i) {
		new (static_cast<A*>(ptr1) + i) A(i * 10);
	}

	// 使用这些 A 对象

	// 销毁这些对象
	for (int i = 0; i < 10; ++i) {
		(static_cast<A*>(ptr1) + i)->~A();
	}
	operator delete[](ptr1);


	return 0;
}

  • operator new创建空间,new构造
  • 我们需要使用 operator delete先销毁空间,再使用delete销毁资源并且是不能直接使用delete来进行销毁空间的,因为我们创建的空间是未定义的,new构造之后我们是会申请资源甚至空间的
  • 如果直接用delect销毁不使用operator delete销毁就会导致空间没有销毁
  • 如果只是使用operator delete销毁空间,就会导致资源没有销毁
  • 并且此时还应该先使用delete来销毁资源 再使用operator delete销毁空间

定位new表达式(placement-new) (了解)

定位new主要使用的区域在于内存池,所以这里作为了解进行学习

一、在特定内存位置构造对象

  1. 可以在预先分配好的内存区域中创建对象,而不依赖于默认的内存分配机制。例如,使用operator new或其他方式分配了一块内存后,可以使用定位new在这块内存上构造对象。
  2. 语法形式为:new (pointer) Type(args...),其中pointer是指向已分配内存的指针,Type是要构造的对象类型,args...是构造函数的参数。
  3. 当使用普通的newdelete操作符时,delete会自动调用对象的析构函数并释放内存。但是对于通过定位new创建的对象,由于没有通过常规的内存分配机制,仅仅使用delete或者operator delete来释放内存不会自动调用析构函数。
  4. 此时我们发现可以用operator new开辟空间,new构造没有那么麻烦了,我们可以直接定位,可以直接看下面代码,但是这里有问题的就是不支持显示构造,但是支持显示析构

#include<iostream>
using namespace std;
class A
{
public:
	A(int capacity = 4, int size = 0)
		:_Capacity(capacity)
		, _size(size)
		, _arr(nullptr)
	{
		//创建空间
		_arr = new int[_Capacity];
		printf("A()");
	}
	~A() 
	{
		//这里释放我们是需要匹配方括号,这里是释放数组形式的内容,自适应找到需要释放的内存
		delete[] _arr;
		_Capacity = 4;
		_size = 0;
		printf("~A()");

	}

private:
	int* _arr;
	int _Capacity;
	int _size;
};

int main()
{
	//正常函数的调用
	A* p = new A[10];
	delete[] p;
	printf("\n");

	//定位的使用,operator new创建一个空间,new定位进行构造
	void* ptr1 = operator new[](10 * sizeof(A));
	new(ptr1) A[10];//定位不支持显示构造,必须有默认构造

	//delete[] ptr1;//定位是需要显示调用析构函数来进行释放资源的。直接使用delect自动释放资源是不能实现的
	for (int i = 0; i < 10; i++)
	{
		//static_cast强制类型转化关键字//<A*>转化为A类型//(ptr1) + i)->~A();循环显示调用析构函数
		(static_cast<A*>(ptr1) + i)->~A();
	}

	operator delete[](ptr1);



	return 0;
}

二、与内存池等技术结合使用

  1. 在一些高性能场景下,为了避免频繁的内存分配和释放开销,可以使用内存池预先分配一大块内存,然后在需要创建对象时使用定位new在内存池中选取合适的位置构造对象。
  2. 这样可以提高内存分配的效率,减少内存碎片的产生。

三、资源管理和对象生命周期控制

  1. 通过定位new,可以更精细地控制对象的构造和析构时机,特别是在一些复杂的资源管理场景中。
  2. 例如,可以在特定的资源初始化后,在与之相关的内存位置构造对象,确保资源和对象的生命周期紧密关联。

四、注意事项

  1. 使用定位new构造的对象,在销毁时需要手动调用析构函数,而不能直接使用delete来释放内存,因为delete会尝试释放由默认内存分配机制分配的内存,而不是定位new所使用的内存。
  2. 例如:new (ptr) Type(args...);构造的对象,在销毁时应该使用ptr->~Type();来调用析构函数。

malloc/free 和 new/delete 的区别

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值