【c++】深度解析new/delete以及new[]/delete[]

C动态内存管理方式

1.堆上:

C语言中使用malloc /calloc/ realloc/ free进行动态内存管理,但对上的内存需要用户自己来管理,动态malloc /calloc/ realloc的空间必须free掉,否则会造成内存泄漏

2.栈上

使用_alloca在栈上动态开辟内存,栈上开辟的内存由编译器自动维护,不需要用户显示释放C

C++动态内存管理方式

1.new操作符

new delete操作符是C++用来动态申请内存的关键

malloc/free和new/delete的区别与联系

  • 它们都是动态管理内存的入口
  • malloc/free是C/C++标准库的函数,new/delete是C++操作符
  • malloc/free只是动态分配内存空间/分配空间,而new/delete除了分配空间还会调用构造函数和析构函数进行初始化与清理(清理成员)
  • malloc/free需要手动计算类型的大小,返回对应类型的指针void *,new/delete/可自己计算类型的大小,返回对应类型的指针

2,操作符new

操作符new是一个函数

void * operator new(size_t size);//allcoate an object
void operator delete(size_t size);//free an object

void * operator new[](size_t size);//allocate an array
void operator delete[](size_t size);//free an array

前面两个均是C++标准函数

总结:

  • operator new/operator delete operator new[]/operator delete[] 和 malloc/free用法一样
  • 他们只负责分配空间/释放空间,不会调用对象构造函数/析构函数来初始化/清理对象
  • 实际operator new和operator delete只是malloc和free的一层封装

3.new和delete背后机制

下面我们用一个实例来来解释

class A
{
public:
	A(int v)
		:var(v)
	{
		fopen_s(&file,,);
	}
	~A()
	{
		fclose(file);
	}

private:
	int var;
	FILE *file;
}

很简单,类A中有两个私有成员,有一个构造函数和一个析构函数,析构函数初始化私有变量var以及打开一个文件。析构函数负责关闭文件

我们使用

class A *pA=new A(10);

来创建一个类的对象,返回其指针pA,如下图所示new背后完成的工作

这里写图片描述

简单总结一下:

  • 首先需要调用上面提到的operator new 标准库函数,传入的参数为A的大小,这里为8个字节,这样函数返回的是分配内存的起始地址,这里假设是0x007da290
  • 上面分配的内存是未初始化的,也是未类型化的,第二步就在这一块原始的内存上对类型进行类对象初始化,调用的是相应的构造函数,这里是调用A:A(10);这个函数,从图中也可以看到对这块申请的内存空间进行了初始化,var=10;file指向打开的文件。最后一步就是返回新分配并构造好的对象的指针,这里pA就指向0x007da290这块内存,pA的类型就为A对象的指针

  • 反汇编

delete解析

delete就做了两件事情:

  • 调用pA指向对象的析构函数,对打开的文件进行关闭
  • 通过上面的提到的标准库函数operator delete来释放该对象的内存,传入的参数为pA的值。也就是0x007da290

总结:

如何申请和释放一个数组

  • 我们经常要用到动态分配一个数组,也许是这样的:
string *psa = new string[10];      //array of 10 empty strings
int *pia = new int[10];           //array of 10 uninitialized ints

 

  • 上面在申请一个数组时都用到了 new [] 这个表达式来完成,按照我们上面讲到的 new 和 delete 知识,第一个数组是 string 类型,分配了保存对象的内存空间之后,将调用 string 类型的默认构造函数依次初始化数组中每个元素;第二个是申请具有内置类型的数组,分配了存储 10 个 int 对象的内存空间,但并没有初始化。
  • 如果我们想释放空间了,可以用下面两条语句:
delete [] psa;
delete [] pia;
  • 都用到delete[]表达式,注意这地方的[]一般情况下不能漏掉,第一个对10个string类对象分别调用析构函数,然后在释放掉为对象分配的所有内存空间;第二个因为是内置类型不存在析构函数,直接释放为10个int型分配的所有内存空间,这里对于第一种情况就有一个问题了,我们如何知道psa指向对象的数组的大小?怎么知道调用几次析构函数?
  • 这个问题直接导致我们需要在new[]一个对象数组时,需要保存数组的维度,C++的做法是分配数组空间时多分配了4个字节的大小,在delete[]时就可以取出这个保存的数,就知道了需要调用析构函数多少次
  • 还是用图来说明比较清楚,我们定义了一个类A,但不具体描述类的内容,这个类中有显示的构造函数,析构函数等,那么当我们调用class A*pAa=new  A[3];需要做的事情如下:

这里写图片描述

总结:

这样的话,释放就很简单了:

这里写图片描述

注意:

  • 调用析构函数的次数是从数组对象指针前面的 4 个字节中取出;
  • 传入 operator delete[] 函数的参数不是数组对象的指针 pAa,而是 pAa 的值减 4。

总结:

 

 

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值