C++:剖析new与delete实现原理

剖析new与delete实现原理:

之前简单写过一篇关于new与delete使用文章:可以先看看这篇文章。
C++:new与delete简单使用

我们先来看一下:malloc与new、delete与free区别
1.malloc与new的区别:
①malloc按字节开辟内存的;new开辟内存时需要指定类型;
②malloc开辟内存返回的都是void * ,new相当于运算符重载函数,返回值自动转为指定的类型的指针。
③malloc只负责开辟内存空间,new不仅仅也有malloc功能,还可以进行数据的初始化。
④malloc开辟内存失败返回nullptr指针;new抛出的是bad_alloc类型的异常。
⑤malloc开辟单个元素内存与数组内存是一样的,都是给字节数;new开辟时对单个元素内存后面不需要[],而数组需要[]并给上元素个数。

2.free和delete的区别:
①free不管释放单个元素内存还是数组内存,只需要传入内存的起始地址即可。
②delete释放单个元素内存,不需要加中括号,但释放数据内存时需要加中括号。
③delete执行其实有两步,先调用析构,再释放;free只有一步。

我们这里主要是对new与delete实现原理进行剖析:
我们写了这样一段代码:

int* p = new int;
delete p;

将其转为反汇编后:
在这里插入图片描述
我们发现,new与delete其本质也是函数的调用。

new -> operator new
delete -> operator delete

那么我们自己实现一下它:
1.operator new的实现:

//new:先调用operator开辟内存空间,然后调用对象的构造函数
void* operator new(size_t size)
{
	void *p = malloc(size);
	if (p == nullptr)
	{
		throw bad_alloc();
	}
	cout << "operator new addr:" << p <<endl;
	return p;
}

//operator new[]实现
void* operator new[](size_t size)
{
	void *p = malloc(size);
	if (p == nullptr)
	{
		throw bad_alloc();
	}
	cout << "operator new[] addr:" << p <<endl;
	return p;
}

2.operator delete的实现:

//delete p:调用p指向对象的析构函数,再调用operator delete释放内存空间
void operator delete(void *ptr)
{
	cout << "operator delete addr:" << ptr <<endl;
	free(ptr);
}
//operator delete[]实现
void operator delete[](void *ptr)
{
	cout << "operator delete[] addr:" << ptr <<endl;
	free(ptr);
}

执行结果:实现成功。
在这里插入图片描述
那么我们实现成功了,产生了几个小小的疑惑。
问题一:平常我们new与delete都是正常使用的,但是new与delete能混用吗?C++为什么区分单个元素和数组的内存分配和释放呢?
情况1:int类型下将其混用

int *p = new int;
delete[]p;

int *q = new int[10];
delete q;

测试一下:能够混用。对于整型来说,没有构造函数与析构函数,针对于int类型,new与delete功能只剩下malloc与free功能,可以将其混用。
在这里插入图片描述
情况2:类类型下将其混用
我们创建一个Test类

class Test
{
public:
	Test(int data = 10):ptr(new int(data))
	{
		cout << "Test()" << endl;
	}
	~Test()
	{
		delete ptr;
		cout << "~Test()" << endl;
	}
private:
	int *ptr;
};

①先来看一下对单个元素正常使用:

Test *p1 = new Test();
delete p1;

测试结果:执行成功。
在这里插入图片描述
②但是如果我们将单个元素与delete[]混用:

Test *p1 = new Test();
delete[]p1;

测试结果:程序崩溃。new与delete不能再混用了。
在这里插入图片描述
③再对数组类型正常使用:

Test *p2 = new Test[5];
delete[]p2;

测试结果:执行成功。
在这里插入图片描述
④最后将数组与delete进行混用:

Test *p2 = new Test[5];
delete p2;

测试结果:程序崩溃。
在这里插入图片描述
我们来仔细分析一下:
       正常情况下,每一个test对象有一个整型成员变量,我们这里分配了5个test对象。delete时先调用析构函数,this指针将正确的对象的地址传入析构函数中,加了[]表示有好几个对象,有一个数组其中每一个对象都要进行析构。但delete真正执行指令时,底层是malloc按字节开辟,并不知道是否开辟了5个test对象的数组,因此还要再多开辟一个4字节来存储对象的个数,假设它的地址是0x100;但是new完之后p2返回的地址是0x104地址,当我们执行delete[]时,会到4字节来取一下对象的个数,将知道了是5个并将这块内存平均分为5份,将其每一份对象起始地址传给相应的析构函数,正常析构,最后将0x100开始的4字节也释放。
       而我们p2出错是给用户返回的存对象开始的起始地址,delete p2认为p2只是指向了一个对象,只将Test[0]对象析构,直接从0x104 free(p2),但底层实际是从0x100开辟的,因此崩溃;而p1出错:p1只是单个元素,从0x104开始开辟内存,但是delete[]p1,里面并没有那么多元素,最后还释放了4个字节的存储对象个数的内存(即从0x100释放)因此崩溃。
在这里插入图片描述

经过我们实践证明:
1.对于普通的编译器内置类型new/delete[]混用是可以的;new[]/delete混用也是可以的。
2.自定义的类类型,有析构函数,为了调用正确的析构函数,那么开辟对象数组时会多开辟4个字节记录对象的个数,不能混用。

问题二:C++中如何设计一个程序检查内训泄露问题?
       核心是用new与delete运算符重载接管整个应用的内存管理,对内存开辟释放都要记录。 检查内存泄露,new操作没有对应的delete,在全局中重写new与delete;new操作中用映射表记录一下都有哪些内存被开辟过了;delete时候,将相应的内存资源用内存地址标识,再删除掉。如果整个系统运行完发现映射表中有一些内存还没有被释放则存在内存泄露。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值