关于C++的new和delete

new的使用

new和malloc的作用一样,都是从堆上申请一块空间。使用malloc时,需要指定申请空间的字节大小

int* p = (int*)malloc(sizeof(int) * 40);//比如开辟40个int的空间

使用起来很麻烦,不仅字节大小要用sizeof,对于返回的地址还要强制类型转换。相比之下new的使用就更加简洁

int main()
{
	int* p1 = new int;// new一个int对象
	int* p2 = new int[10];// new十个int对象
	int* p3 = new int(1);// new一个int对象并初始化成1
	int* p4 = new int[10]{ 1,2,3 };// new十个int对象,并初始化成括号里的内容
	return 0;
}

在这里插入图片描述
相应的,如果申请了多个对象的空间,释放时也要对所有空间进行释放

int main()
{
	int* p1 = new int;// new一个int对象
	int* p2 = new int[10];// new十个int对象
	int* p3 = new int(1);// new一个int对象并初始化成1
	int* p4 = new int[10]{ 1,2,3 };// new十个int对象,并初始化成括号里的内容
	delete p1;// 释放一个对象的空间
	delete[] p2;// 释放多个对象的空间
	delete p3;
	delete[] p4;
	return 0;
}

如果申请了多个对象,但只释放一个对象,很可能导致内存泄漏。所以动态内存的申请与释放要对应的使用

如果new char[100],但使用delete而不是delete[],delete只释放一次内存(调用一次自定义类型的析构函数),这样可能导致程序的崩溃。

与C语言的malloc的差别

通过调试对比两者区别

class Stack
{
public:
	Stack(int capacity = 4)
	{
		_data = new int[capacity];
		_capacity = capacity;
		_top = 0;
	}
	~Stack()
	{
		delete[] _data;
		_capacity = 0;
		_top = 0;
	}
private:
	int* _data;
	int _capacity;
	int _top;
};

int main()
{
	Stack* ps1 = new Stack;
	Stack* ps2 = (Stack*)malloc(sizeof(Stack));
	if (ps2 == nullptr)
		exit(-1);

	delete ps1;
	free(ps2);
	return 0;
}

首先Stack中的_data是用来存放数据的,并且是new出来的空间。在这里插入图片描述
可以看到new出来的Stack(ps1指针)的_capacity和_top都进行了初始化,而malloc出来的Stack(ps2指针)没有进行初始化,都是随机值。原因是:当new的对象类型是内置类型时,malloc和new没有区别,但new一个自定义类型时,new不仅会开辟空间,还会调用该类型的构造函数(也就是将new出来的对象进行了初始化),而malloc只负责开空间,初始化需要我们自己写。

对于空间的释放,delete会先调用Stack的析构函数,再去释放ps1指针,而free会直接释放ps2指针。

但Stack中的_data还开辟了空间,如果直接释放ps2,_data指向的空间就无法释放,此时出现内存泄漏。所以使用delete更省事,只要类型的析构函数正确的释放成员变量申请的空间,用delete释放内存就一行代码的事,但用free的话,要先去释放_data(类的成员变量申请的空间,但该成员如果是私有的则不能直接访问),再去释放ps2。显然在C++使用new和delete是更爽的,编译器为我们做的事实在是太多了。

(还有一个区别,new和delete是操作符,而malloc和free是函数)

new和delete的底层原理

operator new和operator delete

new和delete是C++中的操作符,operator new和operator delete是C++中的全局函数,new在底层会调用operator new,delete同理。

以下的代码是operator new和operator delete在库中的实现

void* __CRTDECL operator new(size_t size) _THROW1(_STD bad_alloc)
{
	// try to allocate size bytes
	void* p;
	while ((p = malloc(size)) == 0)
		if (_callnewh(size) == 0)
		{
			// report no memory
			// 如果申请内存失败了,这里会抛出bad_alloc 类型异常
			static const std::bad_alloc nomem;
			_RAISE(nomem);
		}
	return (p);
}
/*
operator delete: 该函数最终是通过free来释放空间的
*/
void operator delete(void* pUserData)
{
	_CrtMemBlockHeader* pHead;
	RTCCALLBACK(_RTC_Free_hook, (pUserData, 0));
	if (pUserData == NULL)
		return;
	_mlock(_HEAP_LOCK); /* block other threads */
	__TRY
		/* get a pointer to memory block header */
		pHead = pHdr(pUserData);
	/* verify block type */
	_ASSERTE(_BLOCK_TYPE_IS_VALID(pHead->nBlockUse));
	_free_dbg(pUserData, pHead->nBlockUse);
	__FINALLY
		_munlock(_HEAP_LOCK); /* release other threads */
	__END_TRY_FINALLY
		return;
}

可以看出operator new在底层调用了malloc(C++利用现有的接口malloc,而不是重新造轮子),operator new实际是malloc的封装,而malloc申请失败,会返回nullptr,但operator new不会返回空指针,而是抛异常,malloc申请成功,函数返回申请空间的地址。
在这里插入图片描述
这就是new的调用过程,delete同理:delete去调用operator delete,operator delete调用free。所以new的实现是1.调用operator new2.调用构造函数。delete的实现是:1.调用operator delete2.调用析构函数

重载专属的operator new

如果有这样的场景:需要多次new对象,new又去调用operator new,而operator new调用的是malloc,malloc从堆上申请空间,每次都从堆上申请空间,这样的成本太大,效率太低。因此可以重载operator new,使operator new从内存池上申请空间,改变operator的底层实现机制,从而节省成本,提高效率(库中有内存池,可以使用)。

定位new

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

如果有这样的场景,operator new了一个Stack,但Stack有_data,operator new不会为_data申请空间(_data的内存空间没有分配),并且_data是私有成员不能直接访问,需要调用Stack的构造函数去申请_data的空间,但构造函数不能显式的调用,构造函数只会在对象定义时被编译器自动调用。但使用定位new可以间接的调用构造函数

使用:new(指针名,指向已经分配过内存的空间)要调用构造函数的类型名(初始化列表)
在这里插入图片描述
ps1是指向Stack的指针,并且Stack的空间已经被operator new分配过了,要调用Stack的构造函数,所以接下来写的是Stack,最后的调用Stack的构造函数要传的参数,如果该构造函数无参,可以不写。

malloc和new的区别

关于两者的区别,从两个角度上分析,一个是用法,一个是底层实现。

1.malloc是函数,但new是操作符
2.使用malloc需要指定开辟空间的大小,new只要跟上要开辟空间的类型
3.使用malloc需要对返回的指针进行强转,new则不需要
4.对于自定义类型,malloc不会对其进行初始化,而new会去调用自定义类型的构造函数进行初始化(delete也是一样,会调用对象的析构函数进行资源的清理,free只会释放对象)
5.malloc申请失败会返回空,需要进行检查,而new申请失败会抛异常

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值