【深入理解C++】new/delete和new[]/delete[]探秘

1.operator new()和operator delete()

在这里插入图片描述

operator new() 和 operator delete() 是函数。

new 干了两个事情:

  1. 在堆中分配内存:通过 operator new() 来分配内存
  2. 调用构造函数来初始化内存

delete 也干了两个事:

  1. 调用析构函数
  2. 释放内存:调用 operator delete() 来释放内存

2.new记录分配的内存大小供delete使用

int *p = new int; // 4字节
delete p;

删除的时候,编译器怎么知道要回收的是 4 字节?

答:new 内部有记录机制,记录了分配出去多少内存。

new 如何记录分配的内存大小供 delete 使用?

答:不同的编译器,new 内部有不同的实现方式。

3.new[]/delete[]申请和释放一个数组

3.1 基本数据类型(内置类型)

举例1:

#include <iostream>
using namespace std;

int main()
{
	int* p = new int(100); // 如果不释放,会泄漏4字节
	delete p;

	return 0;
}

举例2:

#include <iostream>
using namespace std;

int main()
{
	int* p = new int[2]; // 如果不释放,会泄漏8字节
	delete[] p;

	return 0;
}

3.2 自定义类型(类类型)

举例1:

#include <iostream>
using namespace std;

class A
{
public:
	A()
	{
		cout << "A::A()" << endl;
	}
	~A()
	{
		cout << "A::~A()" << endl;
	}
};

int main()
{
	A* p = new A(); // 如果不释放,会泄漏1字节
	delete p;

	return 0;
}

在这里插入图片描述

举例2:

#include <iostream>
using namespace std;

class A
{
public:
	A()
	{
		cout << "A::A()" << endl;
	}
	~A()
	{
		cout << "A::~A()" << endl;
	}
};

int main()
{
	A* p = new A[2](); // 如果不释放,会泄漏6字节
	delete[] p;

	return 0;
}

在这里插入图片描述

疑问:为什么给类型 A 对象数组动态分配内存时多出来 4 个字节,而给内置类型 int 数组动态分配内存时并没有多出来 4 字节?

在上面的程序中,对于类类型 A,调用了两次构造函数、两次析构函数,也就是说,delete一个数组时,要为每一个数组元素调用析构函数。

但是,对于 delete 表达式,它并不知道数组的元素个数,只有 operator new() 函数和 operator delete() 函数知道。因此,必须有一种手段来告诉 delete 表达式的数组大小是多少。

那么,一种可行的方式就是,多分配一个大小为 4 字节的空间来记录数组大小,同时可以约定前 4 字节来记录大小。那么,由 operator new() 函数分配的地址与 new 表达式返回的地址应该相差 4 个字节。

当然,对于非类类型数组和不需要调用析构函数的类类型数组,这多余的 4 字节就不需要了。

4.new/delete和new[]/delete[]要配对使用

举例1:

#include <iostream>
using namespace std;

int main()
{
	int* pi = new int[3]; // 如果不释放,会泄漏12字节
	//delete pi; // 这里即使用delete释放,也不会发生内存泄漏	
	delete[] pi; // 这种释放方法才是最规范的

	return 0;
}

举例2:

#include<iostream>
using namespace std;

class A
{
public:
	A()
	{
		cout << "A::A()" << endl;
	}

	// 没有自定义的析构函数
};

int main()
{
	A* pa = new A[2](); // 如果不释放,会泄漏2字节
	//delete pa; // 这里即使用delete释放,也不会发生内存泄漏
	delete[] pa; // 这种释放方法才是最规范的

	return 0;
}

举例3:

#include <iostream>
using namespace std;

class A
{
public:
	A()
	{
		cout << "A::A()" << endl;
	}
	~A()
	{
		cout << "A::~A()" << endl;
	}
};

int main()
{
	A* pa = new A[2](); // 如果不释放,会泄漏6字节
	//delete pa; // 这里如果用delete释放,系统就会报告异常
	delete[] pa; // 这种释放方法才是最规范的

	return 0;
}

在这里插入图片描述

为什么自己一提供析构函数,不用 delete[] 来释放 new[] 出来的内存就报异常呢?

从上图中可以看出,只调用了 1 次 A 的析构函数而不是 2 次,表示肯定有内存泄漏。真正释放内存的是 operator delete() 函数,而多出来的 4 个字节导致释放内存空间错乱。

结论:如果一个对象(内置对象、类对象),使用 new[] 来分配内存,却用单独的 delete(而不是 delete[])来释放内存,那么这个对象需要满足的条件是:对象的类型要么是内置类型或者无自定义的析构函数的类类型。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值