深入理解new和delete的原理

malloc和new的区别

(1)malloc按字节开辟内存的,new开辟内存时需要指定类型,所以malloc开辟内存返回的都是void*,new直接返回类型指针
(2)malloc只负责开辟空间,new不仅有malloc的功能,还可以进行数据的初始化
(3)malloc开辟内存失败返回nullptr指针;new抛出bad_alloc类型的异常(所以不能用nullptr判断开辟开辟空间是否失败)

free和delete的区别

(1)delete:先调用析构函数,再释放内存,free只释放内存

注意:普通的数据类型new/malloc delete/free没区别,因为没有析构函数,比如 int* p;char * ch;

new和delete底层

(1)底层其实就是再调用重载运算符函数
new -> operator new(重载运算符)的调用
delete->operator delete(重载运算符)的调用
在这里插入图片描述
我们自己实现一下底层:

#include<iostream>
using namespace std;
//先调用operator new开辟内存空间,再调用对象的构造函数(初始化)
void* operator new(size_t size)
{
	void* p = malloc(size);
	if (p == nullptr)
		throw bad_alloc();
	return p;
}
//delete p; 先调用p指向对象的析构函数,再调用operator delete释放内存空间
void operator delete(void* ptr)
{
	free(ptr);
}
int main()
{
	try
	{
		int* p = new int;
		delete p;
	}
	catch (const bad_alloc& err)
	{
		cerr << err.what() << endl;
	}
	return 0;
}

总结:
单个对象new:先调用operator new开辟内存空间,再调用对象的构造函数(初始化)
单个对象delete: 先调用p指向对象的析构函数,再调用operator delete释放内存空间

new[]和delete[]底层

#include<iostream>
using namespace std;
void* operator new[](size_t size)
{
	void* p = malloc(size);
	if (p == nullptr)
		throw bad_alloc();
	return p;
}

void operator delete[](void* ptr)
{
	free(ptr);
}
int main()
{
	try
	{
		int* q = new int[10];
		delete[] q;
	}
	catch (const bad_alloc& err)
	{
		cerr << err.what() << endl;
	}
	return 0;
}

面试题

1. 如何检测内存泄漏
接管内存管理,重写operator newoperator delete,使用映射表记录下申请了哪些内存释放了哪些内存,就可以很直观的看到哪些内存是没有被释放的

2.new/delete[]new[]/delete可以混用吗?C++为什么区分单个元素和数组的内存分配释放呢
(1)对于普通的编译器内置类型,没有构造和析构可言,只涉及内存开辟释放,内存开辟释放底层就是malloc和free,所以可以混用
(2)对于自定义类型,有析构函数,为了正确调用析构函数,再开辟数组的时候会多开辟4字节,记录对象个数,不能混用
下面进行详细解释假设有一个类:

class Test
{
public:
	Test() { cout << "Test()" << endl; };
	~Test() { cout << "~Test()" << endl; };
private:
	int ma;
};
int main()
{
	Test* p = new Test[5];
	delete []p;

	return 0;
}

在我们执行new Test[5]操作的时候,实际上内存不止开辟 5*sizeof(Test)字节的空间,而是会多开辟四字节的空间记录我们申请的对象个数,方便知道析构次数。
在这里插入图片描述

如图:
执行new Test[5]会开辟5*sizeof(Test)+4字节的内存空间,红色区域用来储存对象个数;new Test[5]返回p地址(对象的起始地址)
void operator delete[](void* ptr)传递p-4地址,从p-4内存地址开始释放(包含存储对象个数区域要一起释放)
这样即知道了需要调用几次析构函数,也可以给内存释放彻底
测试代码:

#include<iostream>
using namespace std;
void* operator new[](size_t size)
{
	cout << "开辟空间大小:" << size << endl;
	void* p = malloc(size);
	if (p == nullptr)
		throw bad_alloc();
	cout << "开辟内存起始地址:" << p << endl;
	return p;
}

void operator delete[](void* ptr)
{
	cout << "释放起始地址:" << ptr << endl;
	free(ptr);
}

class Test
{
public:
	Test() { cout << "Test()" << endl; };
	~Test() { cout << "~Test()" << endl; };
private:
	int ma;//占四个字节
};
int main()
{
	Test* p = new Test[5];
	cout << "用户拿到的地址:" << p << endl;
	delete[] p;

	return 0;
}

在这里插入图片描述

下面这样是有问题的
在这里插入图片描述
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值