菜鸟笔记
提升您的技术认知

初识c 内存管理-ag真人游戏

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的定义表达式进行显示调构造函数进行初始化

 

网站地图