C/C++内存管理(new,delete)详解

一,C/C++内存分布

在这里插入图片描述

1.内核空间:放的是与操作系统相关的代码,用户没有权限直接去操作;如果要操作,只能通过指定的api函数进行。
2.栈:存放与函数调用相关的一些数据;
栈帧:函数的参数,函数体中的局部变量,一些寄存器信息。特性:函数调用完成之后,对应的栈帧就被回收了。
3.内存映射段:放置静态库动态库的数据等。
4.堆:用户进行动态内存申请。C语言中:malloc/calloc/realloc。这些空间用完之后,必须要通过free来进行释放,堆空间相对而言比较大。
5.数据段:存放全局数据(变量和对象),被static修饰数据。
特性:当程序启动时,该部分数据的空间就被开辟好了,当程序结束时被销毁—>即该位置存储的数据生命周期伴随程序。
6.代码段:放置用户代码,以及只读常量。该位置的数据是不能修改的。

二,C语言中动态内存管理方式

1.malloc/calloc/realloc这三个方法之间的区别??

相同点:

1.都是C语言中用来进行动态内存申请的库函数
2.申请的空间都在堆上,用完之后必须要使用free来进行释放
3.如果空间申请成功,返回空间的首地址,如果申请失败返回的是NULL,因此在使用之前必须要进行判空。
4.返回值类型都是void*,在接收返回值时,必须要进行强转。

不同点:

void* malloc(size_t size);
  malloc的参数size是用户所申请空间的字节数;
  申请空间成功返回空间的首地址,如果申请空间失败,返回的是NULL;
  用户在进行接收的时候必须要强转,在使用时必须要进行判空,使用完之后必须要借助free来进行释放。
void* calloc(size_t num, size_t size);
  参数个数不同:num:表示元素的个数。 size:表示单个元素的所占的字节数
  功能上唯一的不同就是:calloc会将其申请的内容空间初始化为0
void* realloc(void* p, size_t size);
将p指向堆空间的大小调整到size字节
    p==NULL-->该函数的功能与malloc类似,直接申请size字节返回即可。
    p!=NULL-->将p指向的空间大小调整到size字节
假设:p指向空间的大小为oldsize字节。
size<=oldsize:将p指向的空间缩小,然后返回原空间的首地址即可
size>oldsize:将p指向的空间扩大
    大一点点:realloc(p,16);直接在p的基础上进行延申
    大许多:realloc(p,400) ;申请size个字节的新空间,将旧空间中的内容拷贝到新空间,释放旧空间,然后返回新空间的地址

三,C++中动态内存管理

前言:C++既然可以兼容C语言,即C语言中动态内存管理的方式在C++中仍旧可以使用。

为什么C++要单独整一套自己的动态内存管理方式呢?

	在C++中,使用malloc/free在堆上申请或者释放内置类型的空间,没有任何问题。
	但是不能采用malloc从堆上申请对象的空间。因为:malloc不会主动调用构造函数
因此该块空间并不能成为对象,而只是与对象大小相同的一块堆空间。
	也不能使用free释放堆对象的空间,因为free在释放对象空间时,不会调用析构函数将对象中的资源清理干净。

C++中动态内存管理的方式:
申请单个类型的空间:new

 int* p1 = new int;
 int* p2 = new int(10); // 对申请的空间进行初始化

释放单个类型空间使用:delete

 delete p1;
 delete p2;

申请一段连续的空间:new []

 int* p3 = new int[10];
 int* p4 = new int[10]{ 1, 2, 3, 4, 5, 6, 7, 8, 9, 0};

释放一段连续的空间: delete[]

 delete[] p3;
 delete[] p4;

注意:

new/delete  new[]/delete[]  malloc/free一定要匹配使用。
否则:程序可能会崩溃或者程序可能会发生内存泄漏

四,new和delete的实现原理

1.内置类型

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

2.自定义类型

1.    new T;

1.申请内存空间
调用void* operator new(size_t size)申请空间

 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(size)函数中直接使用malloc循环来申请空间,
    成功:直接返回
    失败:检测用户是否提供内存不足的应对措施:
         是:执行用户所给的操作,然后继续使用malloc来进行申请
         否:该方法会抛出bad_alloc异常

2.调用构造函数对申请的空间进行初始化

2.	 delete P;

1.调用对应类型的析构函数,清理对象中的资源

2.调用void operator delete(void* p)
operator delete最终是通过free来释放空间的.

3.	 new T[n]

1.申请空间
先调用void* operator new[](size_t size)来申请空间
注意:如果T类中定义了析构函数,在使用new[]申请空间时会多申请4个字节
2.调用n次构造函数,将申请的内存空间的中的n个对象构造好

4.	 delete[] p;

1.调用n次析构函数对p指向的空间中的资源进行清理
2.调用void operator delete[](void* p)对p指向的空间进行释放

注意:先创建的后释放

五,定位new表达式

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

使用格式:

new (place_address) type或者new (place_address) type(initializer-list)
place_address必须是一个指针,initializer-list是类型的初始化列表
使用场景:
	定位new表达式在实际中一般是配合内存池使用。因为内存池分配出的内存没有初始化,所以如果是自定义
类型的对象,需要使用new的定义表达式进行显示调构造函数进行初始化。
class Test
{
public:
	Test(int t = 0)
		:_t(t)
		, _p(new int)
	{
		cout << "Test(int):" << this << endl;
	}

	~Test()
	{
		delete _p;
		cout << "~Test():" << this << endl;
	}
private:
	int _t;
	int* _p;
};
int main()
{
	// 注意:malloc申请出来的空间不能将其称作为对象
	// pt现在指向的是与Test对象相同大小的一段空间
	// 因为:malloc在申请空间期间不会调用构造函数
	Test* pt = (Test*)malloc(sizeof(Test));

	// 现在需要将pt指向的堆空间变成一个对象
	// 如果能够在pt指向的空间上执行构造函数即可
	new(pt)Test(100);

	pt->~Test();   //调用析构函数
	free(pt);
	return 0;
}
  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值