为什么有必要写自己的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;//静态成员的声明不能带staticAirPlane::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;
}

验证结果:

总结

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

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

  1. MySQL DELETE 语句的一个简单介绍

    MySQL DELETE 语句 你可以使用 SQL 的 DELETE FROM 命令来删除 MySQL 数据表中的记录. 你可以在 mysql> 命令提示符或 PHP 脚本中执行该命令. 语法 ...

  2. 类的operator new与operator delete的重载

    为什么有必要写自己的operator new和operator delete? 答案通常是:为了效率. 缺省版本的operator new是一种通用型的内存分配器,它必须可以分配任意大小的内存块.同样 ...

  3. Cpp 对象模型探索 / operator new、operator delete、operator new[] 和 operator delete [] 重载

    零.前言 对于函数 operator new 和 operator delete 来说,分为全局重载和局部重载. 全局重载的形式: void *::operator new(size_t size); ...

  4. operator framework搭建operator开发环境

    简介 operator framework 是一个为云原生开发者提供开发operator的组件框架的开源项目,它目前包含三个组件: Operator SDK.集成controller-runtime, ...

  5. python operator 多属性排序_又碰到一个非常实用的模块,以后的各种运算就用它了,python内置的常用包。

    在工作中,经常对数据进行各种运算,如要从一个序列中返回一个新的序列,亦或是要对两个数进行比较或者进行加和操作等.如果只是一个简单的运算,怎么都好办.但如果我们面对的是比较复杂的需求时,可能我们更多的是 ...

  6. C++ new和delete(C++动态分配和释放内存)

    当我们需要为类对象动态分配存储空间时,我们应该使用 C++语言提供的 new 与 new[] 操作符,而不要使用 C语言提供的 malloc() 函数. 虽然 malloc() 函数具有分配存储空间的 ...

  7. 自己做的一个固定大小对象内存池,效率大概为原始的new/delete的2倍

    提升不高,不过好处是可以多次申请小对象,一次释放.(只适应于无动态申请资源的class) vs2012测试情况如下: // CHchFixLenMemPool.h #pragma once #ifnd ...

  8. 研一寒假02-指针_new分配内存_使用new来创建动态数组_使用动态数组_使用delete来释放new分配的内存...

    #---------------------------------指针-----------------------------------# #include <iostream> i ...

  9. linux delete内存不下降_linux内存分配管理

    linux内存分配管理 一.前言 作为从事与C/C++程序开发人员,我们一直需要很好的管理内存,申请和释放:可能很多只知道使用malloc.new去申请,使用free.delete去释放,但是,去根究 ...

最新文章

  1. 【分享】Java的几个重要词语
  2. sql 数组三 展平数组
  3. 【GDKOI2003】分球
  4. 第十四章:详解Jenkins节点配置
  5. 搭建基础架构-QueryRule
  6. node+koa2+mysql搭建博客后台
  7. centos7python命令_02.将python3作为centos7的默认python命令
  8. 猎豹浏览器打飞机_墙内最好浏览器,微软带来完整版谷歌浏览器,扩展、同步无限制!...
  9. SharePoint2013的头像显示和读取
  10. 微信小程序 js 正则校验车牌号 车牌号校验
  11. 20170716xlVba销售明细转销售单据
  12. linux文件复制粘贴权限,Linux 文件特殊权限
  13. html5编辑器(图片编辑器基于canvas)
  14. Gentoo 软件包冲突
  15. c++ 工厂模式简介和应用场景
  16. cocos2d 由导出文件.csb反推出cocosUI工程
  17. 弗吉尼亚理工大学计算机科学,美国弗吉尼亚理工大学计算机科学本科.pdf
  18. 韩天峰php教程,韩天峰 - Swoole4-全新的PHP编程模式
  19. ElementUI 的 el-select 设置值后显示value而不是label
  20. 360前端星计划学习-html

热门文章

  1. 阿里主动改革,再次引领国内公司治理新浪潮
  2. matlab求图像暗通道图像,基于暗通道先验条件图像去雾算法
  3. 责任链设计模式Demo
  4. 电脑出现“损坏的图像”窗口提示dll没有被指定在Windows上运行如何解决
  5. 【论文笔记】(JSMA)The Limitations of Deep Learning in Adversarial Settings
  6. 2014年末,和小小在一起
  7. 区块链的设计模式 blockchain SMR BCML DML
  8. vue-cli项目设置浏览器地址栏图标的几种方法以及不同的域名使用不同的域名logo和域名title
  9. Scope Hoisting(范围提升)
  10. 超声检测的A扫描、B扫描、C扫描