重载operator new和operator delete一例

视频空降

老师代码如图
在该例中,我们使用重载operator new / delete的目的(或者说在该例中体现出来的重载的作用):
记录每个A类对象在创建时,堆区里现存在的A类对象个数。
先来看重载operator new / delete的做法,带注释版代码如下:(上图代码中将两种方法放在一块了)

#include<iostream>
using namespace std;
class A {
public:
	A() {};
	A(const A& rhs) {};//具体不做实现了
	static void* operator new(size_t);
	static void operator delete(void*,size_t);//new和delete操作不依赖于具体对象,故声明为静态
	static int newCount;
	static int deleteCount;//这两个变量用于跟踪堆区里的A类对象数目
};
void* A::operator new(size_t size) {
	char* p = new char[size + sizeof(int)];//这里额外分配了一个int的空间来存放“堆区现存A对象个数”,这个空间会跟着每个new出来的对象的指针
	//or
	//char*p =(char*)::operator new(size+sizeof(int));
	*(int*)(p + size) = newCount - deleteCount;//由于p类型为char,步长恰为一个字节,所以“p+size”刚好指向我们额外设置的int位,再将其转换为int*,因此对其解引用时可以操作四个字节(sizeof)(int))
	++newCount;
	return p;
}
void A::operator delete(void* p, size_t size) {
	++deleteCount;
	delete[](char*)p;//先将p转换成char*类型是因为在分配空间时,就是以char数组的形式分配的空间,而new出来的动态数组,会自动设置一个位置记录数组规模(包含了几个对象)
}
int A::deleteCount = 0;
int A::newCount = 0;
int main() {
	//测试一下
	A* p1 = new A;//堆区默认构造一个,"堆区现存A对象"应为0
	A temp; A* p2 = &temp;//栈区构造一个
	A* p3 = new A(*p2);//堆区拷贝构造一个,"堆区现存A对象"应为1

	cout << "在堆区创建次数:" << A::newCount << endl;
	cout << "在堆区释放次数:" << A::deleteCount << endl;

	cout << "对象1:" << *(int*)(p1 + 1) << endl;
	cout << "对象2:" << *(int*)(p2 + 1) << endl;//由于p2不是new来的,所以p2向后移动1单位后,其实就越界了,得到了一个无效数据,也就是如果对象不是创建在堆,其实我们是没有为他设置这个存放“dui中现存A对象数目”的int位的,这也算是一个瑕疵吧
	cout << "对象3:" << *(int*)(p3 + 1) << endl;

	//释放
	if (p1) {
		delete p1;
		p1 = NULL;
	}
	if (p3) {
		delete p3;
		p3 = NULL;
	}
	cout << "释放后再来看一下:\n";
	cout << "在堆区创建次数:" << A::newCount << endl;
	cout << "在堆区释放次数:" << A::deleteCount << endl;

	return 0;
}

执行结果:
在这里插入图片描述

这里由于我们是要统计当创建A对象时,当前在堆里的A对象个数。这里我们要统计的数据与new/delete操作息息相关,所以很自然的就能想到应该在这两个操作上做手脚。
期间进行的一系列细节的处理更是叫人拍手叫绝:在堆中额外分配空间来存储一个int(我叫他“暗度陈仓”[滑稽]),几次指针类型的转换等。
下面在原代码中添加一个不使用这种思路,而是通过在类内部中设置成员记载该数据的反例作为对比:

#include<iostream>
using namespace std;
class A {
public:
	A() {
		existCount = newCount - deleteCount - 1;
	};
	A(const A& rhs) {
		existCount = newCount - deleteCount - 1;
	};//具体不做实现了
	static void* operator new(size_t);
	static void operator delete(void*,size_t);
	int existCount;
	static int newCount;
	static int deleteCount;
};
void* A::operator new(size_t size) {
	char* p = new char[size + sizeof(int)];/
	//or
	//char*p =(char*)::operator new(size+sizeof(int));
	*(int*)(p + size) = newCount - deleteCount;
	++newCount;
	return p;
}
void A::operator delete(void* p, size_t size) {
	++deleteCount;
	delete[](char*)p;
}
int A::deleteCount = 0;
int A::newCount = 0;
int main() {
	//测试一下
	A* p1 = new A;//堆区默认构造一个,"堆区现存A对象"应为0
	A temp; A* p2 = &temp;//栈区构造一个,"堆区现存A对象"应为1
	A* p3 = new A(*p2);//堆区拷贝构造一个,"堆区现存A对象"应为1

	cout << "在堆区创建次数:" << A::newCount << endl;
	cout << "在堆区释放次数:" << A::deleteCount << endl;

	cout << "下面是“暗度陈仓”的结果:\n";
	cout << "对象1:" << *(int*)(p1 + 1) << endl;
	cout << "对象2:" << *(int*)(p2 + 1) << endl;//由于p2不是new来的,所以p2向后移动1单位后,其实就越界了,得到了一个无效数据
	cout << "对象3:" << *(int*)(p3 + 1) << endl;

	cout << "下面是使用成员变量来存储的结果: \n";
	cout << "对象1:" << p1->existCount<<endl;//应为0
	cout << "对象2:" << p2->existCount << endl;//应为1
	cout << "对象3:" << p3->existCount << endl;//应为1

	//释放
	if (p1) {
		delete p1;
		p1 = NULL;
	}
	if (p3) {
		delete p3;
		p3 = NULL;
	}
	cout << "释放后再来看一下:\n";
	cout << "在堆区创建次数:" << A::newCount << endl;
	cout << "在堆区释放次数:" << A::deleteCount << endl;

	return 0;
}

运行结果:
在这里插入图片描述
可以看到:在栈区创建的对象2,其创建时,堆区已经有对象p1,但是existCount却被设置为0(1+0-1),这在逻辑上是行不通的。

或许该例子涉及的问题有更高明的处理方式,但我觉得这个例子中体现的一些思想是很好的,对重载operator new / delete的讲解是很到位的。

本文仅作本人复习巩固用,能力有限,如有纰漏,烦请指出

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值