new/delete实现原理 | 定位new表达式(placement-new) —— 了解

264 阅读2分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第10天,点击查看活动详情

一、new和delete的实现原理

💦 内置类型

如果申请的是内置类型的空间,new 和 malloc,delete 和 free 基本类似,不同的地方是:new/delete 申请和释放的是单个元素的空间,new[] 和 delete[] 申请的是连续空间,而且 new 在申请空间失败时会抛异常,malloc 会返回 NULL。

💦 自定义类型

new 的原理:

  • 调用 operator new 函数申请空间
  • 在申请的空间上执行构造函数,完成对象的构造

delete 的原理:

  • 在空间上执行析构函数,完成对象中资源清理的工作
  • 调用 operator delete 函数释放对象的空间

new T[N] 的原理:

  • 调用 operator new[] 函数,在 operator new[] 中实际调用 operator new 函数完成 N 个对象空间的申请
  • 在申请的空间上执行 N 次默认构造函数

delete[] 的原理:

  • 在释放的对象空间上执行 N 次析构函数,完成 N 个对象中资源清理的工作
  • 调用 operator delete[] 释放空间,实际在 operator delete[] 中调用 operator delete 来释放空间

场景使用 ❓

class Stack
{
public:
	Stack(int capacity = 4)
		: _a(new int[capacity])
		, _size(0)
		, _capacity(capacity)
	{
		cout << "Stack(int capacity = 4)" << endl;
	}
	~Stack()
	{
		delete[] _a;
		_size = _capacity = 0;	
		cout << "~Stack()" << endl;	 
	}
private:
	int* _a;
	int _size;
	int _capacity;
};
int main()
{
	//1
	Stack st;

	//2
	Stack* ps = new Stack;
	delete ps;
	
	return 0;
}

📝说明: 在这里插入图片描述

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

定位 new 表达式是在已分配的原始内存空间中调用构造函数初始化一个对象。

使用格式:

  • new(place_address) type 或者 new(place_address) type(initializer-list)
  • place_address 必须是一个指针,initializer-list 是类型的初始化列表

如果想对 malloc 开辟的已有的一块空间去调用构造函数 ❓

struct ListNode
{
	int val;
	struct ListNode* next;
	ListNode(int x)
		: val(x)
		, next(nullptr)
	{
		cout << "ListNode(int x)" << endl;
	}
	~ListNode()
	{
		cout << "~ListNode()" << endl;	
	}
};
int main()
{
	//实例化一个对象构造函数、析构函数自动调用
	ListNode node(1);
	//new调用构造函数、delete调用析构函数
	ListNode* p = new ListNode(2);
	delete p;

	//显示调用构造函数、析构函数
	ListNode* n1 = (ListNode*)malloc(sizeof(ListNode));
	new(n1)ListNode(3);
	n1->~ListNode();
	free(n1);
	return 0;
}

📝说明

使用场景:

定位 new 表达式在实际中一般是配合内存池使用。因为内存池分配出的内存没有初始化,所以如果是自定义 类型的对象,需要使用 new 的定义表达式进行显示调构造函数进行初始化。

这里举一个好理解的 —— 复制一份 a数组到另一块空间 pb ❗

class A
{
public:
	A(int a = 0)
		: _a(a)
	{
		cout << "A()" << this << endl;	
	}
	A(const A& a)
	{
		cout << "A(const A& a)" << this << endl;	
	}
	A& operator=(const A& a)
	{
		cout << "A& operator=(const A& a)" << this << endl;
		return *this;
	}
	~A()
	{
		cout << "~A()" << this << endl;
	}
private:
	int _a;
};
int main()
{
	//构造+赋值
	A a[5];
	A* pb = new A[5];
	for(int i = 0; i < 5; i++)
	{
		pb[i] = a[i];
	}
	delete []pb;
		
	//拷贝构造
	A* pd = (A*)malloc(sizeof(A) * 5);
	for(int i = 0; i < 5; i++)
	{
		new(pd + i)A(a[i]);	
	}
	for(int i = 0; i < 5; i++)
	{
		(pd+i)->~A();	
	}
	
	return 0;
}

📝说明 在这里插入图片描述

由此可见,使用定位 new 表达式的效率更高。