1. new 和delete
在堆上开辟空间,c语言中我们要用到malloc,并且使用方式较为繁琐:
( int* )malloc(sizeof( int ) * 4)
且calloc/realloc 的方式也都相同,详细点击这里:动态内存管理分析理解
而在c 中使用 new 可以简化使用方式:
new 一个对象
new 类型
int* p1 = new int;
初始化
new 类型后面 (值)
int* p1 = new int(5);
new 多个对象
new 类型 [个数]
int* p2 = new int[10];
初始化
new 类型 [个数] {初始化的值}
int* p3 = new int[10]{1, 2, 3, 4, 5, 6};
delete 释放
要注意格式匹配
像单个对象,直接delete 即可
delete p1;
如果是多个对象,delete []
delete[] p2; delete[] p3;
c 内存开辟释放和c中开辟释放有何区别?
对于内置类型来说:new 和 malloc 并没有什么区别,只是用法不同;
对于自定义类型来说,new更加方便,会自动调用其构造函数,相当于开辟空间加初始化:
而利用初始化列表,创建构造函数,使用new ,可以大大简化这些步骤:
delete和free 对内置类型的处理方式也没什么区别
但是对于自定义类型而言
delete 对于自定义类型是会调用它自己的析构函数:
这两个函数不是new 和 delete 的重载,只是名字的区别
new和delete是用户进行动态内存申请和释放的操作符
operator new 和operator delete是系统提供的全局函数
new在底层调用operator new全局函数来申请空间
delete在底层通过operator delete全局函数来释放空间
2.1 operator new
实际上operator new函数是malloc函数封装后的成果
此函数不会调用构造函数
和malloc相同点
都是函数、都会开辟空间
使用方法和malloc相同
和malloc不同点
通过malloc来申请空间,当malloc申请空间成功时直接返回,
申请空间失败,不会返回空,会抛异常;
这一点就跟new相同了
new底层原理
开辟空间,失败抛异常 调用构造函数
这两个功能合并到一块就构成了new
而开辟空间失败抛异常其实调用的就是封装malloc后的operator new函数。
operator new 调用构造函数 = new
2.2 operator delete
实际上operator delete函数是free函数封装后的成果
此函数不会调用析构函数
为了跟operator new匹配对应
operator delete 调用析构函数 = delete
2.3 operator new与operator delete的类专属重载
在项目中往往存在一直需要开辟空间的时候,就比如在链表中插入数据,每次new一个节点,都会在堆上申请,但是如果数据量较大,那么效率是会变低的,来看一个测试:
struct listnode { listnode* _next; listnode* _prev; int _data; listnode(int data = 0) :_next(nullptr) , _prev(nullptr) , _data(data) { //记录节点数 cout << "listnode" << endl; } }; class list { public: list() { _head = new listnode; _head->_next = _head; _head->_prev = _head; } void push(int val) { //每一次插入数据,都需要new一个节点 listnode* newnode = new listnode; listnode* tail = _head->_prev; //链接 tail->_next = newnode; newnode->_prev = tail; newnode->_next = _head; _head->_prev = newnode; } ~list() { listnode* cur = _head->_next; while (cur!=_head) { listnode* next = cur->_next; delete cur; cur = next; } delete _head; _head = nullptr; } private: listnode* _head; }; int main() { list l; int n = 1000; for (int i = 0;i
为了提高速度,创建operator new与operator delete的类专属重载;
在构造节点时,会自动调用operator new的重载,清空时自动调用与operator delete的重载;
实现这两个重载函数,用到的是内存池,使用内存池申请和释放内存,提高效率;
在之后会了解到,这里先主要是以重载为主,稍作了解:
struct listnode { listnode* _next; listnode* _prev; int _data; listnode(int data = 0) :_next(nullptr) , _prev(nullptr) , _data(data) { //记录节点数 cout << "listnode" << endl; } void* operator new(size_t n) { void* p = nullptr; p = allocator
().allocate(1); cout << "memory pool allocate" << endl; return p; } void operator delete(void* p) { allocator ().deallocate((listnode*)p, 1); cout << "memory pool deallocate" << endl; } };
operator new 函数是调用 new 时的一部分,那我们单独调用operator new 进行空间开辟时,又不好初始化,因为构造函数不能显示调用。
使用格式
new (指针) 类型;
new (指针) 类型(初始化列表);
定位new什么时候用?
那么我们直接调用 new 不香吗,非得用operator new 定位new?
定位new表达式在实际中一般是配合内存池使用:
因为内存池分配出的内存没有初始化,
所以如果是自定义类型的对象,
需要使用new的定义表达式进行显示调构造函数进行初始化