C&C++ 动态内存管理详解

目录

C语言的动态内存管理:

1.malloc

2.calloc

3.realloc

4.free

使用事项

C++动态内存管理

开辟内置类型数据空间

开辟自定义类型数据空间

 new和delete实现原理

operator new 和 operator delete

内置类型的数据的实现原理

自定义类型的数据的实现原理

定位new表达式

格式

实例

malloc/free和new/delete的区别

相同点

不同点


C语言的动态内存管理:

在C语言中,动态内存的管理主要靠四个函数来实现,这四个函数分别是malloc、calloc、realloc、free。他们分别有不同的功能,但总体上都是在内存的堆上进行内存操作。

1.malloc

结构:

参数是开辟空间的大小,单位是字节(bite),如果开辟成功,则返回开辟空间的首地址,类型是void*,开辟失败就返回空指针NULL。

2.calloc

结构:

calloc函数和malloc函数的功能相近,但是calloc函数会把开辟的空间给赋值成0,其参数分别是开辟空间中元素的个数(num)和每个元素所占空间的大小(size),单位是字节(bite)。 如果开辟成功,则返回开辟空间的首地址,类型是void*,开辟失败就返回空指针NULL。

3.realloc

结构:

realloc函数是给已经分配好的空间重新分配空间,其参数是空间的地址(memblock)和空间的大小(size),单位是字节(bite)。如果开辟成功,则返回开辟空间的首地址,类型是void*,开辟失败就返回空指针NULL。

4.free

结构:

free函数是对动态开辟的空间进行销毁,即数据销毁,把空间权限开放给操作系统,使操作系统能够再次拥有这片空间的分配权和使用权。参数是空间地址(memblock),无返回值。 

使用事项

1.calloc函数初始化只能是0,对于元素是指针的情况会将元素初始化为NULL,元素是浮点数的情况下会初始化为0.0。

2.realloc函数用来增容时是在已存在的空间后追加空间,如果后面没有足够的空间的话会在内存其他的地方寻找足够的空间,并把原来的数据拷贝过去。而当realloc函数用来缩小空间时,因缩小而失去后面的空间,而这些空间里的数据则变为随机值,并且无法合法访问。

3.为避免内存泄漏,当程序最后结束的时候要对动态开辟的空间进行释放操作,也就是调用free函数。

C++动态内存管理

由于C++是面向对象的语言,因此会经常使用到类。而类里面的成员变量多数被访问限定为私有(private),无法在动态开辟的时候在类的外部直接初始化,因此C++引入了newdelete来解决这样和类似的问题。

开辟内置类型数据空间

#include <iostream>
using namespace std;
int main()
{
	int* pa = new int; //申请一个元素的空间
	int* pb = new int(1);  //申请一个元素的空间并初始化为1
	int* pArrey = new int[10]; //申请10个元素的空间
	int* p = new int[10]{ 1,2,3,4,5,6,7,8,9,10 };//申请多个元素的空间并初始化
	cout << *pb << endl;
	delete pa;
	delete pb;
	delete[] pArrey; //销毁多个元素的时候要加[]
	delete[] p;
	return 0;
}

格式:

开辟单个元素空间:new  数据类型

开辟单个元素空间并初始化:new  数据类型(初始值)

开辟多个元素空间:new  数据类型[n],n为元素个数

开辟多个元素空间并初始化:new  数据类型[n] {num1, num2, ... ,num},n为元素个数,num1, num2, ... ,num为初始值,未初始化的元素默认为零。

注:

申请和释放单个元素的空间,使用new和delete操作符,申请和释放连续的空间,使用new[]和 delete[]。

开辟自定义类型数据空间

首先先来看一段代码:

#include <iostream>
using namespace std;
class A
{
public:
	A(int val = 1)
		:_val(val)
	{
		cout << "A()" << endl;  //表明只要调用A的构造函数就输出A()
		_a = new int[10];
	}
	~A()
	{
		cout << "~A()" << endl;  //表明只要调用A的析构函数就输出~A()
		delete[] _a;
		_val = 0;
	}
private:
	int* _a;
	int _val;
};
int main()
{
	A* pa = new A(10);
	delete pa;
	return 0;
}

运行结果:

可以看出在new和delete自定义类型数据的时候,调用的是类的构造函数析构函数

这也就意味着当我们自己写好构造函数时,我们在主程序里用new动态开辟空间时,会自动调用构造函数给他初始化。

再看另一种特殊情况:类里含有自定义类型的数据

#include <iostream>
using namespace std;
class A
{
public:
	A(int val = 1)
		:_val(val)
	{
		cout << "A()" << endl;
		_a = new int[10];
	}
	~A()
	{
		cout << "~A()" << endl;
		delete[] _a;
		_val = 0;
	}
private:
	int* _a;
	int _val;
};
class B
{
private:
	A _a1;
	A _a2;
};
int main()
{
	B* pb = new B;
	delete pb;
	return 0;
}

运行结果:

可以看出调用了两次 “自定义类型A” 的构造函数和析构函数 ,只因为B里面的成员变量有两个A类数据。也就是说,new动态开辟时会自动调用自定类型数据的构造函数和析构函数

小结:

在申请自定义类型的空间时,new会调用构造函数,delete会调用析构函数,而malloc与free不会。

 new和delete实现原理

事实上,new和delete都是在malloc和free函数的基础上实现的,但又不是简单的直接使用malloc和free,这里就要谈到operator new 和  operator delete这两个全局函数了!

operator new 和 operator delete

new和delete是用户进行动态内存申请和释放的操作符,operator new 和operator delete是系统提供的 全局函数,new在底层调用operator new全局函数来申请空间,delete在底层通过operator 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;
}
/*
free的实现
*/
#define free(p) _free_dbg(p, _NORMAL_BLOCK)

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

内置类型的数据的实现原理

如果申请的是内置类型的空间,new和malloc,delete和free基本类似,不同的地方是:new/delete申请和释放的是单个元素的空间,new[]和delete[]申请的是连续空间,而且new在申请空间失败时会抛异常, malloc会返回NULL。

自定义类型的数据的实现原理

1.new

调用operator new函数申请空间,之后在申请的空间上执行构造函数,完成对象的构造。

2.delete

在空间上执行析构函数,完成对象中资源的清理工作,之后调用operator delete函数释放对象的空间。

3.new T[N]

调用operator new[]函数,在operator new[]中实际调用operator new函数完成N个对象空间的申请,之后在申请的空间上执行N次构造函数

4.delete[] 

在释放的对象空间上执行N次析构函数,完成N个对象中资源的清理,之后调用operator delete[]释放空间,实际在operator delete[]中调用operator delete来释放空间。

定位new表达式

如果我们用malloc为自定义类型的数据开辟一段空间,开辟之后并没有构造对象,只是有了相应类型的空间了。这个时候就需要使用定位new了。

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

格式

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

实例

#include <iostream>
#include<stdlib.h>
using namespace std;
class A
{
public:
	A(int val = 1)
		:_val(val)
	{
		cout << "A()" << endl;
		_a = new int[10];
	}
	~A()
	{
		cout << "~A()" << endl;
		delete[] _a;
		_val = 0;
	}
private:
	int* _a;
	int _val;
};
class B
{
private:
	A _a1;
	A _a2;
};
int main()
{
	B* pb = (B*)malloc(sizeof(B));
	new(pb) B;
	free(pb);
	return 0;
}

运行到 new(pb) B; 时的结果:

 相当于实例化开辟的空间了。

malloc/free和new/delete的区别

相同点

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

不同点

1. malloc和free是函数,new和delete是操作符。

2. malloc申请的空间不会初始化,new可以初始化。

3. malloc申请空间时,需要手动计算空间大小并传递,new只需在其后跟上空间的类型即可。

4. malloc的返回值为void*, 在使用时必须强转,new不需要,因为new后跟的是空间的类型。

5. malloc申请空间失败时,返回的是NULL,因此使用时必须判空,new不需要,但是new需要捕获异常。

6. 申请自定义类型对象时,malloc/free只会开辟空间,不会调用构造函数与析构函数,而new在申请空间后会调用构造函数完成对象的初始化,delete在释放空间前会调用析构函数完成空间中资源的清理。

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值