C++内存管理——malloc/calloc/realloc/new/delete/定位new

c++中程序内存区域的划分

在这里插入图片描述

栈:又叫堆栈,非静态局部变量/函数参数/返回值等等,栈是向下增长的
堆:用于程序运行时动态内存分配,堆是可以上增长的
数据段:存储全局数据和静态数据。
代码段:可执行的代码/只读常量。

int main()
{
	char arr[] = "abcd";
	const char* arr2 = "abcd";
	return 0;
}

arr在哪里?*arr在哪里?
arr2在哪里?*arr2在哪?
arr是属于常量字符串拷贝过去然后初始化他,实际上还是在栈上,所以不管是arr还是 *arr都是在栈上
但是arr2存的是常量字符串的地址,其本身是在栈上的,但是*arr2指向的是常量字符串

malloc/calloc/realloc区别

点这里

new/delete

C语言中的动态内存管理方式在c++中依然可以使用,但是由于c++中引入了类和对象,此时c++又产生了自己的内存管理方式:通过new和delete操作符进行动态内存管理

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

	//申请完空间后一定得释放空间,如果不匹配那么可能会报错
	delete p1;
	delete[]p2;
	delete p3;
	delete[]p4;




	return 0;
}

new与malloc对于内置类型的空间申请都是一样的,只是对于自定义类型的空间申请还是有所不同的。
假设目前有一个最简单的类

class Date
{
public:
	Date(int year = 1, int month = 1,int day = 1)
		:_year(year)
		,_month(month)
	{
		_day = day;
		//为了区分在new其对象时调用自动调用了拷贝构造
		cout << "Date(int year = 1, int month = 1,int day = 1)" << endl;
	}
	void Print()
	{
		cout << _year << "-" << _month << "-" << _day << endl;
	}
	~Date()
	{
		//为了区分在new其对象时调用自动调用了析构

		cout << "~Date()" << endl;
	}

	    
private:
	int _year;
	int _month;
	int _day;

};

下面同时用new和malloc同时为这个类申请一个对象

int main()
{
	Date*d1 = (Date*)malloc(sizeof(Date));
	assert(d1);
	Date* d2 = new Date;// 开空间+调用构造函数初始化
	d1->Print();
	d2->Print();
	free(d1);
	delete d2;//调用析构函数清理资源 + 释放空间
	return 0;
}

在这里插入图片描述

所以在申请自定义类型空间时,new在开完空间后会自动调用构造函数,delete会调用完析构函数后再释放空间

在malloc开空间失败的会返回空指针,所以每次在调用完malloc后会检查一下此时指针是否为空指针,但是new开空间失败会直接抛异常

operator new与operator delete函数

上述中了解到new和delete是操作符,其底层原理靠的就是operator new与operator delete函数,

operator new是一个函数,并不是运算符重载

operatot new
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 new的源码,我们可以了解到该函数实际通过malloc来申请空间,当malloc申请空间成功时直接返回;申请空间失败,
尝试执行空 间不足应对措施,如果改应对措施用户设置了,则继续申请,否则抛异常。

//operator delete源码
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底层也是通过_free_dbg实现的,再来看看free底层的源码

#define free(p) _free_dbg(p, _NORMAL_BLOCK)

所以说operator delete 本质底层上调用的还是free

综上:

new = operator new+构造函数
delete = 析构函数+operator delete

new X[N]原理:
调用operator new[]函数,在operator new函数中完成对N个对象空间的申请,然后在申请的空间上执行N次构造函数
delete []原理:
在释放的对象空间上执行N次析构函数,完成N个对象中资源的清理。

定位new与池化技术

先打个比方,假设现在堆区是妈妈的钱包,一片空间(你自己开辟好的)相当于是你自己的钱包,然后向堆区中申请空间相当于我们每天一日三餐的饭钱,如果我们每一餐都问老妈要,ok,老妈最后肯定都会给,但是次数太多,频繁的话,不说老妈烦不烦,单单是效率就非常底,一个程序多次向堆区中申请与释放空间也是这样,直接想堆区中申请释放空间的话效率非常底,所以便有了池化技术
由于现在所学有限,池化技术以后再更新
什么是定位new?
定位new表达式是在已分配的原始内存空间中调用构造函数初始化一个对象。
定位new表达式在实际中一般是配合内存池使用。因为内存池分配出的内存没有初始化,所以如果是自定义
类型的对象,需要使用new的定义表达式进行显示调构造函数进行初始化

使用格式:
new (place_address) type或者new (place_address) type(initializer-list)
place_address必须是一个指针,initializer-list是类型的初始化列表

因为如果我们是new一个自定义对象的话,且不说new是直接向堆区中申请空间,而且其会自动初始化,但是如果我们是malloc一个自定义类型的话,我们后面就无法初始化了,因为构造函数是不能显示调用的,但是如果我们如果用定位new(定位new使用的时候是向内存池中使申请空间)的话即使空间开辟后没有及时初始化,那也没事。
eg:

Date* d1 = (Date*)operator new(sizeof(Date));
	//针对一个空间,显示调用构造函数初始化。
	new(d1)Date(5);//等价于 Date*d1 = new Date(5);

malloc/free和new/delete的区别

**同:**都是从堆上申请空间,并且需要用户手动释放空间
异:
1,首先malloc/free是函数,new/delete是操作符
2,new只是单纯的申请空间但是new除了申请空间之外还初始化其申请的空间,free只是释放空间,delete其先调用其所要释放空间的析构函数,然后再释放空间
3,malloc申请空间时需要手动计算空间大小并传递,new直接后面跟空间的类型即可
4,malloc的返回值是void*调用时需要进行强制转换,new只需要在后面跟上空间类型即可
5,malloc申请空间失败时会返回空,new申请空间失败时直接抛异常

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

每天少点debug

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值