实现自己的operator new和operator delete以及实现一个简单的内存池管理类

为什么有必要写自己的operator new和operator delete?

为了效率。缺省的operator new和operator delete具有非常好的通用性,它的这种灵活性也使得在某些特定的场合下,可以进一步改善它的性能。
当调用operator new来分配对象时,得到的内存可能要比存储这个对象所需的要多。因为operator new和operator delete之间需要传递信息。确幸版本的operator new是一种通用型的内存分配器,他必须能够分配任意大小的内存块。operator delete也要可以释放任意大小的内存块。operator delete想弄清它要释放的内存有多大,就必须知道当初operator new分配的内存有多大。一种常用的方法就是让operator new来告诉operator delete当初分配的内存大小是多少,就是在它返回的内存里预带一些额外信息,用来指明被分配的内存块的大小。

在类内实现的内存池

如果软件运行在一个内存很宝贵的环境中,可以专门实现一个operator new,分配一大块内存,再将其分割给链表,new时分配一个节点,delete时再将这个节点放回到链表中。

#pragma once
#include <iostream>
#include <string>
using std::cout;
using std::endl;
using std::string;

class AirPlaneRep
{
public:
	AirPlaneRep();
	~AirPlaneRep();

private:
	int size;//随便写几个成员
	string name;
	double value;
};

AirPlaneRep::AirPlaneRep()
{
}

AirPlaneRep::~AirPlaneRep()
{
}


class AirPlane
{
public:
	AirPlane();

	static void* operator new(size_t size);
	static void operator delete(void* deadObj);

	~AirPlane() {};

private:
	union 
	{
		AirPlaneRep* rep;//指向实际对象
		AirPlane* next;//用于没被使用的对象(放在链表中时指向下一个内存块)
	};
	//指定申请的一大块内存可以放几个对象
	static const int BLOCK_SIZE = 512;
	static AirPlane * head_of_list;//静态指针指向分配的一大块内存,不用在析构函数里释放,静态类型的生存周期结束时,自动释放
};

AirPlane * AirPlane::head_of_list;//静态成员的声明不能带static

AirPlane::AirPlane()
{
	cout << "进入构造" << endl;
}

void * AirPlane::operator new(size_t size)
{
	//把"错误"大小的请求转给::operator new(),比如派生类可能会调用到这个函数
	if (size != sizeof(AirPlane))
	{
		return ::operator new(size);
	}
	AirPlane* p = head_of_list;//第一次为NULL,跳到else分配指定大小的内存
	//如果p可用,说明已经分配过空间了
	if (p)
	{
		cout<<"使用一个链表节点"<<endl;
		head_of_list = p->next;
		//return p;
	}
	else
	{
		cout << "第一次使用,分配一大块内存" << endl;
		//自由链表为空,则使用全局operator new分配一个大的内存块
		//如果申请失败,则在此处调用set_new_handler机制
		AirPlane * newBlock = static_cast<AirPlane*>(::operator new(BLOCK_SIZE * sizeof(AirPlane)));
		//接下来将内存块配置成一个自由链表
		//跳过第一个小内存块,因为它要被返回
		for (int i = 1; i < BLOCK_SIZE; i++)
		{
			newBlock[i].next = &newBlock[i + 1];
		}
		//用空指针结束链表
		newBlock[BLOCK_SIZE - 1].next = NULL;
		p = newBlock;
		head_of_list = &newBlock[1];
	}
	return p;
}

inline void AirPlane::operator delete(void * deadObj)
{
	cout << "进入自定义delete" << endl;
	if (deadObj == NULL)
		return;
	if (sizeof(*(static_cast<AirPlane*>(deadObj))) != sizeof(AirPlane))
	{
		cout << "不是基类airplane类型,调用全局delete对象!" << endl;
		::operator delete(deadObj);
		return;
	}
	AirPlane *dead = static_cast<AirPlane*>(deadObj);
	//将其放在头节点前边,作为头节点,就是放回内存池,不是真的释放空间
	cout << "放回内存池" << endl;
	dead->next = head_of_list;
	head_of_list = dead;
}

测试代码:

// AirPlane.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//

#include "pch.h"
#include <iostream>
#include "AirPlane.h"

int main()
{
    std::cout << "Hello World!\n"; 
	AirPlane *a1 = new AirPlane();
	AirPlane *a2 = new AirPlane();
	delete a1;
	cout << "TEST END!!!" << endl;
	cout << "退出程序之时,静态变量生存期结束,释放申请的内存池" << endl;
}

// 运行程序: Ctrl + F5 或调试 >“开始执行(不调试)”菜单
// 调试程序: F5 或调试 >“开始调试”菜单

// 入门提示: 
//   1. 使用解决方案资源管理器窗口添加/管理文件
//   2. 使用团队资源管理器窗口连接到源代码管理
//   3. 使用输出窗口查看生成输出和其他消息
//   4. 使用错误列表窗口查看错误
//   5. 转到“项目”>“添加新项”以创建新的代码文件,或转到“项目”>“添加现有项”以将现有代码文件添加到项目
//   6. 将来,若要再次打开此项目,请转到“文件”>“打开”>“项目”并选择 .sln 文件

测试结果:
在这里插入图片描述可以看到,首先分配内存,再构造对象。且delete对象时,内存放回了内存池中。

上述内存池内嵌在了airplane类中,为了提高代码重用性,可以写一个简单的内存池分配器类。需要的类直接包含该类即可。

简单实现内存池分配器类

此处使用vector<T*>来实现一个简单的内存池,首先将申请的T大小的空间的指针放在vector中,需要时弹栈一个,不需要时放回vector中。
因为此处存放申请到的内存的不是静态指针,所以需要在析构函数中释放掉。
同时为了看一下内存池耗尽情况,将默认内存池大小修改为2。

#pragma once
#include <iostream>
#include <vector>
using namespace std;

template<typename T>
class Pool
{
public:
	//构造大小为n个T的内存池对象
	Pool(int num = 2 ,int up = 1) : up_attr(up), block_size(num)
	{
		init_mem(block_size);
	}

	void init_mem(int num)
	{
		try 
		{
			int i = 1;
			for (; i <= num; i++)
			{
				T * tmp = static_cast<T *>(malloc(sizeof(T)));
				mempool.push_back(tmp);
			}
		}
		catch (const std::bad_alloc&)
		{
			cout << "内存分配失败" << endl;
			exit(-1);
		}
	}

	void get_more_mem()
	{
		init_mem(block_size * 2 + up_attr);
		block_size += block_size * 2 + up_attr;
		++up_attr;
	}
	
	//为一个对象分配足够的内存
	T * alloc()
	{
		cout << "内存池分配一个对象所需空间" << endl;
		if (mempool.size()==0)
		{
			cout<<"内存池耗尽,需再次申请内存空间"<<endl;
			get_more_mem();
		}
		T* tmp = mempool.back();
		mempool.pop_back();
		return tmp;
	}

	//将p所指的内存返回到内存池中
	void free(void* p)
	{
		p = NULL;
		mempool.push_back(static_cast<T*>(p));
	}

	//释放内存池中全部内存
	~Pool()
	{
		for (int i = 0; i < mempool.size(); i++)
		{
			::free(mempool[i]);
		}
	}

	//验证使用函数
	int get_block_size()
	{
		return block_size;
	}
	//验证使用函数
	int size()
	{
		return mempool.size();
	}
private:
	int block_size;
	int up_attr;//放大因子
	vector<T*> mempool;
};

此时,再去实现上面那个类,就比较简单了,只需要调用内存池类的方法即可。

使用这个内存池

#pragma once
#include "Pool.h"

class AirPlaneRep
{
public:
	AirPlaneRep(){}
	~AirPlaneRep(){}

private:
	string name;
};


class AirPlane
{
public:
	AirPlane() {};
	~AirPlane() {};
	static void* operator new(size_t size);
	static void operator delete(void *p);
	//验证需要放在public中(或写一个公有函数返回它),否则无法访问,实际应该放在private中
	static Pool<AirPlane> my_pool;
private:
	//static Pool<AirPlane> my_pool;
	AirPlaneRep* rep;//指向实际对象
};

Pool<AirPlane> AirPlane::my_pool;

void* AirPlane::operator new(size_t size)
{
	return my_pool.alloc();
}

inline void AirPlane::operator delete(void * p)
{
	cout << "释放对象(将其所用空间放回到内存池中)" << endl;
	my_pool.free(p);
}

验证:

// Pool.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//

#include "pch.h"
#include <iostream>
#include <string>
#include "AirPlane.h"

int main()
{
    std::cout << "Hello World!\n"; 
	cout << "内存池空间:" << AirPlane::my_pool.get_block_size() << "个T" << endl;
	cout << "内存池剩余空间" << AirPlane::my_pool.size()<<"个T" << endl;
	AirPlane *a1 = new AirPlane;
	cout << "内存池空间:" << AirPlane::my_pool.get_block_size() << "个T" << endl;
	cout << "内存池剩余空间" << AirPlane::my_pool.size() << "个T" << endl;
	AirPlane *a2 = new AirPlane;
	cout << "内存池空间:" << AirPlane::my_pool.get_block_size() << "个T" << endl;
	cout << "内存池剩余空间" << AirPlane::my_pool.size() << "个T" << endl;
	AirPlane *a3 = new AirPlane;
	cout << "内存池空间:" << AirPlane::my_pool.get_block_size() << "个T" << endl;
	cout << "内存池剩余空间" << AirPlane::my_pool.size() << "个T" << endl;
	delete a1;
	cout << "内存池空间:" << AirPlane::my_pool.get_block_size() << "个T" << endl;
	cout << "内存池剩余空间" << AirPlane::my_pool.size() << "个T" << endl;
	delete a2;
	cout << "内存池空间:" << AirPlane::my_pool.get_block_size() << "个T" << endl;
	cout << "内存池剩余空间" << AirPlane::my_pool.size() << "个T" << endl;
	delete a3;
	cout << "内存池空间:" << AirPlane::my_pool.get_block_size() << "个T" << endl;
	cout << "内存池剩余空间" << AirPlane::my_pool.size() << "个T" << endl;
}

验证结果:

在这里插入图片描述

总结

基本符合预期,使用、放回、耗尽情况下都得到了验证,完成了内存池的基本功能。后续抽时间会写一个复杂的、功能更完备的内存池。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值