C++ |【总结归纳三本书籍系列】一文透彻资源管理,动态内存分配【下】....

本文已参与「新人创作礼」活动,一起开启掘金创作之路。

一、使用operator new/delete

1、对operator进行重载的作用

1)用来检测运用上的错误

使用new可能出现的问题:

  • 若使用new,可能出现delete失败,而导致内存泄漏
  • new的内存,若出现多次delete将会导致未定义行为;

而使用operator new

  • 可将会持有一串动态分配的地址,而operator delete将会容易检测出错误用法;

2)为了强化效能

operator new不但可用于长时间执行的程序,也可用于少于1秒的程序

3)为了收集使用上的统计数据

可轻松收集分配区块的带下分布情况,寿命分布情况,倾向哪个次序分配和归还,运用型态是否随时间改变;

2、自定义operator new/delete(案例)

在许多情况下需要自定义(全局性/class专属)来提高程序对内存操作的效率;

  • 为了检测运用错误
  • 为了收集动态分配内存值使用统计信息
  • 为了增加分配和归还的速度
    • 自定义将针对与某特定类型的对象设计的;
  • 为了降低缺省内存管理带来的空间额外开销
    • 泛用型内存管理器内部会使用其他内存开销;
  • 为了弥补缺省分配器中的非最佳齐位
    • 泛用型内存管理器的动态分配采取的齐位并不确定;
  • 为了将相关对象成簇集中
    • 将数据集中成簇在尽可能少的内存页中;
  • 为了获得非传统的行为
    • 实现一些功能,而不使用传统的编译器定义的功能;

3、如果创建管理内存池

3.1 第一版使用

使用next指针;

#include<iostream>

/***************************实现重载new***************************/
class MyTestOper
{
public:
	/** 有参构造,将不会提供默认构造 */
	MyTestOper(int x) : i(x){};
	
	/** 获取i的接口 */
	int get_i(){ return i; }

	/** 重载new
	==> 实现空间合理分配
	*/
	void* operator new(size_t);

	/** 重载delete */
	void operator delete(void *);
private:
	MyTestOper *next;
	static MyTestOper* freeP;
	static const int chunkNum;		// 内存块的大小
private:
	int i;
};

MyTestOper* MyTestOper::freeP = 0;
const int MyTestOper::chunkNum = 24;

/** 重载new
==> 实现空间合理分配
*/
void *MyTestOper::operator new(size_t size)
{
	MyTestOper *p = 0;
	if (!freeP)	// 判断内存是否为空
	{
		size_t chunk = size * chunkNum;			// 块的大小
		freeP = p = reinterpret_cast<MyTestOper*>(new char[chunk]);
		/* 将内存切割 */
		int x = 0;
		for (; p != &freeP[chunkNum - 1]; ++p)
			p->next = p + 1;
		p->next = 0;	// 此时p指向尾部
	}

	p = freeP;				// 将p指向头部
	freeP = freeP->next;	// 而freeP指向下一个
	return p;
}

/** 重载delete */
void MyTestOper::operator delete(void * delP)
{

	(static_cast<MyTestOper *>(delP))->next = freeP;
	freeP = static_cast<MyTestOper *>(delP);
}


/***************************不重载***************************/
class MyTestT
{
public:
	/** 有参构造,将不会提供默认构造 */
	MyTestT(int x) : i(x){};

	/** 获取i的接口 */
	int get_i(){ return i; }

private:
	MyTestT *next;
	static MyTestT* freeP;
	static const int chunkNum;		// 内存块的大小
private:
	int i;
};


void main()
{
	std::cout << "--------------重载的结果--------------" << std::endl;
	std::cout << sizeof(MyTestOper) << std::endl;
	size_t const N = 10;
	MyTestOper *p[N];

	for (int i = 0; i < N; ++i)
	{
		p[i] = new MyTestOper(i);
		std::cout << p[i] << std::endl;
	}
	for (int i = 0; i < N; ++i)
	{
		delete p[i];
	}

	std::cout << "--------------不重载的结果--------------" << std::endl;
	std::cout << sizeof(MyTestT) << std::endl;
	MyTestT *pt[N];

	for (int i = 0; i < N; ++i)
	{
		pt[i] = new MyTestT(i);
		std::cout << pt[i] << std::endl;
	}
	for (int i = 0; i < N; ++i)
	{
		delete pt[i];
	}

	system("pause");
}
复制代码

在这里插入图片描述

扫描二维码关注公众号,回复: 14122711 查看本文章

3.2 第二版使用

借用union,将一个对象前4个字节当作指针(next)来使用,通过该指针能够将其他的内存进行衔接;一旦该内存被分配出去则前4个字节的next将不要被需要;

在这里插入图片描述

#include<iostream>

using namespace std;

class Airplane {
private:
	struct AirplaneRep{
		unsigned long miles;
		char type;
	};

	union {
		AirplaneRep rep;
		Airplane* next;
	};
	
public:
	unsigned long getMiles() { return rep.miles; }
	char getType() { return rep.type; }
	void set(unsigned long m, char t) {
		rep.miles = m;
		rep.type = t;
	}

public:
	static void* operator new(size_t size);
	static void operator delete(void* deadObject, size_t size);

private:
	static const int BLOCK_SIZE;		// 块大小
	static Airplane* headOfFreeList;	// 链表
};

Airplane* Airplane::headOfFreeList;
const int Airplane::BLOCK_SIZE = 512;

/* 申请内存块 */
void* Airplane::operator new(size_t size){
	if (size != sizeof(Airplane))
		return ::operator new(size);

	Airplane* p = headOfFreeList;
	if (p)
		headOfFreeList = p->next;

	else{
		/* 申请一大块内存 */
		Airplane* newBlock = static_cast<Airplane*>(
			::operator new(BLOCK_SIZE * sizeof(Airplane))
			);
		/* 切割内存块 */
		for (int i = 1; i < BLOCK_SIZE - 1; ++i) {
			newBlock[i].next = &newBlock[i + 1];
		}
		newBlock[BLOCK_SIZE - 1].next = 0;
		p = newBlock;
		// 指向头部
		headOfFreeList = &newBlock[1];
	}
	return p;
}

/* 回收一个内存块,将它放到链表前端 */
void Airplane::operator delete(void* deadObject, size_t size) {
	if (deadObject == 0)	return;
	if (size != sizeof(Airplane)){
		::operator delete(deadObject);
		return;
	}
	Airplane* carcass = static_cast<Airplane*>(deadObject);
	carcass->next = headOfFreeList;
	headOfFreeList = carcass;
}

/***************************不重载***************************/
class MyTestT
{
public:
	/** 有参构造,将不会提供默认构造 */
	MyTestT(){};


private:
	struct AirplaneRep{
		unsigned long miles;
		char type;
	};

	union {
		AirplaneRep rep;
		Airplane* next;
	};

};


int main()
{
	size_t const N = 10;
	std::cout << "--------------重载的结果--------------" << std::endl;
	std::cout << sizeof(Airplane) << std::endl;
	Airplane *p[N];

	for (int i = 0; i < N; ++i)
	{
		p[i] = new Airplane;
		std::cout << p[i] << std::endl;
	}
	for (int i = 0; i < N; ++i)
	{
		delete p[i];
	}

	std::cout << "--------------不重载的结果--------------" << std::endl;
	std::cout << sizeof(MyTestT) << std::endl;
	MyTestT *pt[N];

	for (int i = 0; i < N; ++i)
	{
		pt[i] = new MyTestT;
		std::cout << pt[i] << std::endl;
	}
	for (int i = 0; i < N; ++i)
	{
		delete pt[i];
	}

	system("pause");
	return 0;
}
复制代码

在这里插入图片描述

3.3 第三版使用

将该内存管理进行提取到一个类中,即不需要在每个类中进行重写,直接引入该类即可使用;

#include<iostream>

using namespace std;

class Allocator{
private:
	struct obj {
		struct obj* next;
	};

public:
	void* allocate(size_t);
	void deallocate(void*, size_t);


private:
	obj* freeStore = nullptr;
	const int CHUNK = 5;
};

/** 分配 */
void* Allocator::allocate(size_t size){
	obj* p;

	if (!freeStore){
		size_t chunk = CHUNK * size;
		freeStore = p = (obj*)malloc(chunk);

		for (int i = 0; i < CHUNK - 1; ++i) {
			p->next = (obj*)((char*)p + size);
			p = p->next;
		}
		p->next = nullptr;
	}
	p = freeStore;
	freeStore = freeStore->next;
	return p;
}

/** 回收 */
void Allocator::deallocate(void* p, size_t size) {
	((obj*)p)->next = freeStore;
	freeStore = (obj*)p;
}

/****************************************测试****************************************/
class Airplane {
private:
	int m_a;
	static Allocator myAlloc;
public:
	Airplane(int i) : m_a(i){}
	static void* operator new(size_t size){
		return myAlloc.allocate(size);
	}

	static void operator delete(void* p, size_t size){
		return myAlloc.deallocate(p, size);
	}
};

Allocator Airplane::myAlloc;

int main()
{
	size_t const N = 10;
	std::cout << "--------------重载的结果--------------" << std::endl;
	std::cout << sizeof(Airplane) << std::endl;
	Airplane *p[N];

	for (int i = 0; i < N; ++i)
	{
		p[i] = new Airplane(i);
		std::cout << p[i] << std::endl;
	}
	for (int i = 0; i < N; ++i)
	{
		delete p[i];
	}

	system("pause");
	return 0;
}
复制代码

在这里插入图片描述

3.4 第四版使用

将该类进一步简化使用,将其定义为宏;

#include<iostream>

using namespace std;

class Allocator{
private:
	struct obj {
		struct obj* next;
	};

public:
	void* allocate(size_t);
	void deallocate(void*, size_t);


private:
	obj* freeStore = nullptr;
	const int CHUNK = 5;
};

/** 分配 */
void* Allocator::allocate(size_t size){
	obj* p;

	if (!freeStore){
		size_t chunk = CHUNK * size;
		freeStore = p = (obj*)malloc(chunk);

		for (int i = 0; i < CHUNK - 1; ++i) {
			p->next = (obj*)((char*)p + size);
			p = p->next;
		}
		p->next = nullptr;
	}
	p = freeStore;
	freeStore = freeStore->next;
	return p;
}

/** 回收 */
void Allocator::deallocate(void* p, size_t size) {
	((obj*)p)->next = freeStore;
	freeStore = (obj*)p;
}


/****************************************定义宏****************************************/

#define DECLARE_POOL_ALLOC()\
	public:\
	void* operator new(size_t size){ return myAlloc.allocate(size); }\
	void operator delete(void* p, size_t size) { return myAlloc.deallocate(p, size); }\
	protected:\
	static Allocator myAlloc;

#define IMPLEMENT_POOL_ALLOC(class_name)\
	Allocator class_name::myAlloc;


/****************************************测试****************************************/
class Airplane {
private:
	int m_a;
	DECLARE_POOL_ALLOC();
public:
	Airplane(int i) : m_a(i){}
};

IMPLEMENT_POOL_ALLOC(Airplane);

int main()
{
	size_t const N = 10;
	std::cout << "--------------重载的结果--------------" << std::endl;
	std::cout << sizeof(Airplane) << std::endl;
	Airplane *p[N];

	for (int i = 0; i < N; ++i)
	{
		p[i] = new Airplane(i);
		std::cout << p[i] << std::endl;
	}
	for (int i = 0; i < N; ++i)
	{
		delete p[i];
	}

	system("pause");
	return 0;
}
复制代码

在这里插入图片描述

4、new_handle

当内存使用不足,无法申请内存时会抛出一个std::bad_alloc exception的异常。但编译器设置,在抛出异常前,可指定调用new_handle

  • 故我们可以使用new_handle对使用的内存进行检查,并即使清理被暂用且没有使用的内存从而让这次的申请成功;
  • 通过set_new_handle传入;

new_handle中一般实现这个两种处理方式:

  • 释放部分内存,让内存被申请成功;
  • 调用abort()exit()
void new_handle()
{
	…………
}

new_handle set_new_handle(new handle p) throw();
复制代码

猜你喜欢

转载自juejin.im/post/7094959091352600613