C++内存管理----new与delete知识总结(超详细)


前言

我们在C语言中通过malloc、realloc、calloc和free函数,对内存进行动态管理。C语言内存管理方式在C++中也可以继续使用,但在C++中我们会有对象的创建和资源清理,此时malloc和free函数,就不能很好满足我们的需求,因此C++又提出了自己的内存管理方式:通过new和delete操作符进行动态内存管理说白了,new和delete就是对malloc和free函数的重新封装。下面我们一起学习。

C语言 动态内存管理(超详解)


一、C/C++内存管理方式

1. new/delete操作内置类型

new和delete是操作符,不是函数。

申请和释放单个元素的空间:

类型指针 指针变量 = new 类型
delete 指针变量

类型指针 指针变量 = new 类型(初始值)
delete 指针变量

申请和释放连续空间:

类型指针 指针变量 = new 类型[元素个数]
delete[] 指针变量

	//类型指针 指针变量 = new 类型
	//申请int类型空间
	int* ptr = new int;
	//释放空间
	delete ptr;
	
	//类型指针 指针变量 = new 类型(初始值)
	//申请int类型空间,并初始化,初始化值为10---->4字节
	int* ptr2 = new int(10);
	delete ptr2;
	
	//类型指针 指针变量 = new 类型[元素个数]
	//申请连续空间,包含10个元素---->40字节,内容为随机值
	int* ptr3 = new int[10];
	//释放连续空间
	delete[] ptr3;

当前对内置类型的操作,和malloc、free函数相差不大,但是通过对自定义类型的操作,就可以看出他们的不同之处。

2. new和delete操作自定义类型

在这里插入图片描述

如上图所示,我们发现malloc只能申请空间,不能进行初始化,而new不仅可以申请空间,还可以进行空间内容初始化,通过调用构造函数进行初始化。下图能清晰的看到调用过程。
在这里插入图片描述free只进行空间的释放,不会进行资源的清理,而delete不仅通过调用析构函数进行资源清理,也会进行空间释放

在自定义类型中,new:先申请对象的空间,再调用构造函数进行空间内容的初始化。

在自定义类型中,delete:先调用析构函数完成对象资源的清理,再释放对象所占空间。

我们上面所说的格式,调用的是默认构造(无参,全缺省)

类型指针 指针变量 = new 类型

如果不是默认构造,代码就会出错:
在这里插入图片描述
这种无默认构造的情况下,我们也可以通过调用带参构造,申请对象空间。参数列表中给出初始值。

类型指针 指针变量 = new 类型(参数列表)

若有多个需要初始化的成员变量,依次在参数列表中给出初始值,分别用逗号隔开。

class A
{
public:
	A(int a, int b, int c)
		:_a(a)
		, _b(b)
		, _c(c)
	{}
private:
	int _a;
	int _b;
	int _c;
};

void test()
{
	A* ptr = new A(10, 20, 30);
	delete ptr;
}

申请连续空间:

类型指针 指针变量 = new 类型[空间个数]
delete[] 指针变量

申请连续空间,需要有默认构造,不能使用带参构造进行多个对象空间的申请和初始化。
在这里插入图片描述我们发现,申请/释放3个连续对象空间,分别会调用3次默认构造,3次析构。

在自定义类型中,申请N个连续对象空间,new:先申请空间,再调用N次默认构造函数。

在自定义类型中,释放N个连续对象空间,delete:先调用N次析构,进行资源清理,再释放空间

二、operator new与operator delete函数

operator new 和operator delete是系统提供的全局函数new在底层调用operator new全局函数来申请空间,delete在底层通过operator delete全局函数来释放空间。

operator new不是new的运算符重载函数,operator delete不是delete的运算符重载函数。operator new与new的使用方式有差异,但是与malloc函数使用方式相似

	//operator new
	//void* operator new(size_t n)
	//void* operator delete(void* ptr)
	int* o_ptr = (int*)operator new(sizeof(int));
	operator delete (o_ptr);

	//malloc
	int* m_ptr = (int*)malloc(sizeof(int));
	free(m_ptr);

	//new
	int* n_ptr = new int;
	delete n_ptr;

operator new与malloc函数最大的区别就是申请空间失败时的处理方式operator new申请失败时,会抛出异常;malloc申请失败时,会返回NULL。

在这里插入图片描述实际上,operator new 通过malloc来申请空间,如果malloc申请空间成功就直接返回,否则执行用户提供的空间不足应对措施,如果用户提供该措施就继续申请,否则就抛异常。operator delete 最终是通过free来释放空间

1. operator new与operator delete的类专属重载

在平时写代码时,可能会申请多个对象空间,如果频繁的向堆上申请和释放内存,会增加系统开销,并且会使系统中出现大量的内存碎片,此时C++就引入了内存池。

内存池就是在程序启动时,预先向堆中申请一部分较大的内存,之后再有内存请求的时候,如果内存池的内存大小能够满足请求,就从内存池里分配,不必再进行系统调用,从而实现性能提升。

//重载operator new
void* operator new(size_t n)
{
	//采用内存池的方式
	allocator<ListNode> alloc;
	return alloc.allocate(1);
}

//重载operator delete
void operator delete(void* ptr)
{
	allocator<ListNode> alloc;
	alloc.deallocate((ListNode*)ptr, 1);
}

在这里插入图片描述我们发现,new申请空间时,确实会调用operator new。delete释放空间时,会调用operator delete。

2. new和delete实现原理

如上面所说,new和delete其实就是operator new与operator delete的封装。

内置类型:

	//new: 调用operator new ----> malloc
	int* ptr = new int;
	//delete: 调用operator delete---> free
	delete ptr;

	//new[]: 调用operator new[] ---> operator new ---> malloc
	int* ptr1 = new int[10];
	//delete[]: 调用operator delete[] ---> operator delete --->free
	delete[] ptr1;

自定义类型:

	//new: 调用operator new ---> malloc,再调用构造函数
	A* ptr = new A;
	//delete: 先调用析构函数,进行资源清理,再调用operator delete ---> free
	delete ptr;

	//new[]: 调用operator new[]---> operator new ---> malloc,再调用N次构造函数
	A* ptr1 = new A[3];
	//delete[]: 调用N次析构,进行资源清理,再调用operator delete[]--->operator delete--->free
	delete[] ptr1;

3. 定位new表达式

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

使用格式:

new (指针) 类型(参数列表)

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

class A
{
public:
	A(int a)
		:_a(a)
	{
		cout << "A(int)" << endl;
	}

	~A()
	{
		cout << "~A()" << endl;
	}
private:
	int _a;
};

void test()
{
   //开辟一个A类型空间,只初始化一个
	A* ptr = (A*)malloc(sizeof(A));
	new (ptr) A(10);
	ptr->~A();
	free(ptr);

    //开辟多个A类型空间,全部初始化
    A* ptr1 = (A*)malloc(sizeof(A)* 5);
	for (int i = 0; i < 5; i++)
	{
		new (ptr1 + i) A(10);
	}

	for (int i = 0; i < 5; i++)
	{
		(ptr1 + i)->~A(); 
	}
	free(ptr1);
}

在这里插入图片描述

三、malloc/free和new/delete的区别

共同点:都是从堆上申请空间,并且需要用户手动释放

区别

  • malloc和free是函数,new和delete是操作符
  • malloc申请的空间不会初始化,new可以初始化
  • malloc申请空间时,需要手动计算空间大小并传递,new只需在其后跟上空间的类型即可
  • malloc的返回值为void*, 在使用时必须强转,new不需要,因为new后跟的是空间的类型
  • malloc申请空间失败时,返回的是NULL,因此使用时必须判空,new不需要,但是new需要捕获异常
  • 申请自定义类型对象时,malloc/free只会开辟空间不会调用构造函数与析构函数,而new在申请空间 后会调用构造函数完成对象的初始化,delete在释放空间前会调用析构函数完成空间中资源的清理
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值