一起养成写作习惯!这是我参与「掘金日新计划 · 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 表达式的效率更高。