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

答案通常是:为了效率。
缺省版本的operator new是一种通用型的内存分配器,它必须可以分配任意大小的内存块。同样,operator delete也要可以释放任意大小的内存块。operator delete想弄清它要释放的内存有多大,就必须知道当初operator new分配的内存有多大。有一种常用的方法可以让operator new来告诉operator delete当初分配的内存大小是多少,就是在它所返回的内存里预先附带一些额外信息,用来指明被分配的内存块的大小。
缺省的operator new和operator delete具有非常好的通用性,它的这种灵活性也使得在某些特定的场合下,可以进一步改善它的性能。尤其在那些需要动态分配大量的但很小的对象的应用程序里,情况更是如此。

内存池:内存池的作用主要也是为了效率。通过一次申请比较大的内存空间,来避免小空间内存的频繁申请和释放,每次需要为对象分配内存空间时,在已经申请的大空间内分配。空闲区被按照对象大小划分为若干块,块与块之间通过链表组织起来。

参考代码:

#include <iostream>
#include <string>
using namespace std;class airplanerep  // 表示一个飞机对象
{
public:airplanerep(int id,const string & s,const string & d){ID = id;start = s;dest = d;}~airplanerep(){cout<<"airplanerep destructor!"<<endl;}int getID() const{return ID;}private:int ID;string start;string dest;
};// 注意airplane和airplanerep含有一样的成员函数,它们的接口完全相同
// 类airplane实际上是个句炳类(Handle class)
class airplane // 修改后的类 — 支持自定义的内存管理
{
public:airplane(int id,const string & s,const string & d){rep = new airplanerep(id,s,d);}~airplane(){delete rep;}int getID() const{return rep->getID();}static void * operator new(size_t size);static void operator delete(void *deadobject,size_t size);private:union{airplanerep *rep;  // 用于被使用的对象airplane *next;    // 用于没被使用的(在自由链表中)对象
    };// 类的常量,指定一个大的内存块中放多少个// airplane对象,在后面初始化static const int block_size;static airplane *headoffreelist;
};airplane *airplane::headoffreelist; //内存池链表头
const int airplane::block_size = 512; //内存池块的个数void * airplane::operator new(size_t size)
{// 把“错误”大小的请求转给::operator new()处理;if(size != sizeof(airplane))return ::operator new(size);airplane *p = headoffreelist; // p指向自由链表的表头// p 若合法,则将表头移动到它的下一个元素if(p)headoffreelist = p->next;else{// 自由链表为空,则分配一个大的内存块,// 可以容纳block_size个airplane对象airplane *newblock = static_cast<airplane*>(::operator new(block_size * sizeof(airplane)));// 将每个小内存块链接起来形成一个新的自由链表// 跳过第0个元素,因为它要被返回给operator new的调用者for (int i = 1; i < block_size-1; ++i)newblock[i].next = &newblock[i+1];// 用空指针结束链表newblock[block_size-1].next = 0;// p 设为表的头部,headoffreelist指向的// 内存块紧跟其后p = newblock;headoffreelist = &newblock[1];}return p;
}// 传给operator delete的是一个内存块, 如果
// 其大小正确,就加到自由内存块链表的最前面
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;
}int main()
{airplane *pa = new airplane(101,"shanghai","beijing"); // 第一次分配: 得到大块内存,生成自由链表,等cout<<pa->getID()<<endl;delete pa;airplane *pb = new airplane(102,"shanghai","beijing");cout<<pb->getID()<<endl;delete pb;return 0;
}

airplane类的说明:

airplane实际上是个句炳类(Handle class),通过指针airplanerep * rep指向一个具体的实现,airplane和airplanerep含有一样的成员函数。句柄类实际上都做了些什么:它只是把所有的函数调用都转移到了对应的主体类中,主体类真正完成工作。
operator new函数负责内存池链表的创建,内存池链表的每个块大小和类airplane一样,每次生成对象的时候分配一个块给对象。
operator delete函数负责收回每个对象的内存块,重新添加到内存池链表。
operator new和operator delete需要同时工作,那么你写了operator new,就也一定要写operator delete。
一个联合(使得rep和next域占用同样的空间),一个常量(指定大内存块的大小),一个静态指针(跟踪自由链表的表头)。表头指针声明为静态成员很重要,因为整个类只有一个自由链表,而不是每个airplane对象都有。
注意:::operator new返回的内存块是从来没有被airplane::operator delete释放。内存泄漏和内存池有一个重要的不同之处:内存泄漏会无限地增长,而内存池的大小决不会超过客户请求内存的最大值。

以上内容基本都来自《Effective C++》,稍作修改。

如果类定义了自己的成员new和delete,类的用户可以通过使用全局作用域确定操作符,强制new和delete使用全局的库函数。如:
Type *p = ::new Type;
::delete p;

如果new使用全局的operator new库函数,那么对应的delete也一定要用全局的operator delete库函数。

一个内存分配器基类

CachedObj的功能:类似于内存池的功能。分配和管理已经分配但未构造对象的自由列表。operator new返回自由列表中的一个元素,当自由列表为空时,operator new分配新的原始内存。operator delete在撤销对象时将元素放回自由列表。

template<class T>
class CachedObj
{
public:void * operator new(size_t sz);void operator delete(void* p, size_t sz);virtual ~CachedObj() {}
protected:T * next;
private:static void add_to_freelist(T * p);static allocator<T> alloc_mem;static T * free_store;static const size_t chunk;
};template<class T>
allocator<T> CachedObj<T>::alloc_mem;template<class T>
T* CachedObj<T>::free_store = NULL;template<class T>
const size_t CachedObj<T>::chunk = 64;template<class T>
void *CachedObj<T>::operator new(size_t sz)
{if(sz != sizeof(T))return ::operator new(sz);if (!free_store){T * array = alloc_mem.allocate(chunk);for(size_t i = 0; i != chunk; ++i)add_to_freelist(&array[i]);}T *p = free_store;free_store = free_store->next;return p;
}template<class T>
void CachedObj<T>::operator delete(void * p,size_t sz)
{if(p == NULL) return;if(sz != sizeof(T)){::operator delete(p);return;}add_to_freelist(static_cast<T*>(p));
}template<class T>
void CachedObj<T>::add_to_freelist(T *p)
{p->next = free_store;free_store = p;
}

如何使用这个类:

// 表示一个飞机对象
class airplanerep: public CachedObj<airplanerep>
{
public:airplanerep(int id,const string & s,const string & d){ID = id;start = s;dest = d;}~airplanerep(){cout<<"airplanerep destructor!"<<endl;}int getID() const{return ID;}private:int ID;string start;string dest;
};int main()
{airplanerep *pa = new airplanerep(101,"shanghai","beijing");cout<<pa->getID()<<endl;delete pa;airplanerep *pb = new airplanerep(102,"shanghai","beijing");cout<<pb->getID()<<endl;delete pb;return 0;
}

作者:阿凡卢
出处:http://www.cnblogs.com/luxiaoxun/
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
http://www.cnblogs.com/luxiaoxun/archive/2012/08/11/2633423.html

类的operator new与operator delete的重载相关推荐

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

    为什么有必要写自己的operator new和operator delete? 为了效率.缺省的operator new和operator delete具有非常好的通用性,它的这种灵活性也使得在某些特 ...

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

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

  3. operator framework搭建operator开发环境

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

  4. C++中的new和delete操作符重载

    文章目录 1 new和delete操作符重载 1.1 new和delete操作符重载简介 1.2 静态存储区中创建动态对象 1.3 在指定的地址上创建C++对象 2 new[]和delete[]操作符 ...

  5. 关于placement new 和 placement delete的重载,以及basic_string重载new()实例

    关于placement new 在https://blog.csdn.net/qq_42604176/article/details/111997397中已经介绍了placement new的形式. ...

  6. [C++]分数类的定义(成员函数、运算符重载)

    [C++]分数类的定义(成员函数.运算符重载) 1 分数类成员和成员函数 1.1 分数类成员 1.2 分数类的成员函数 1.3 分数类的io操作 1.4 分数类的预定义 2 成员函数定义 2.1 化简 ...

  7. C++类与对象笔记十二:运算符重载二:左移运算符重载

    左移运算符重载:可以打印输出自定义数据类型. 为了输出重载,我们先看看现有的输出函数.输出类型为std下的ostream类型的引用. 标准输出流(全局只能有一个). 返回值类型为ostream,函数名 ...

  8. C#基础--类/接口/成员修饰符,多态、重载、重写,静态和非静态

    C#基础--类/接口/成员修饰符,多态.重载.重写,静态和非静态 类/接口/成员修饰符 C#修饰符---接口: 接口默认访问符是internal 接口的成员默认访问修饰符是public C#修饰符-- ...

  9. operator new和operator delete

    从STL源码剖析中看到了operator new的使用 template<class T> inline void _deallocate(T* buffer) {::operator d ...

最新文章

  1. TIME_WAIT状态及存在原因
  2. 事件响应的优先级、stopProgapation禁止下层组件响应
  3. des加密解密JAVA与.NET互通实例
  4. python对字符串(内嵌的字典)转为字典
  5. mysql blob hex_数据库的完整备份与恢复 quot;--hex-blobquot; - - ITeye博客
  6. HDU 1754线段树
  7. spring boot / cloud (七) 使用@Retryable来进行重处理
  8. xml文件的三种解析方式 DOM SAM PULL
  9. ASP.NET伪静态的方法及相关资料
  10. 传递给 left 或 substring 函数的长度参数无效_MySQL:函数入门实例
  11. sybase 性能监控及调优(转)
  12. Win32 API 列表5 (格式有点乱)
  13. Java 动态眨眼 EyesJPanel (整理)
  14. Android软键盘工具类
  15. ERNIE1.0 与 ERNIE2.0 论文解读
  16. 5款不妨一试的硬盘碎片整理工具
  17. 扩展CUDA SDK 2.3 の convolutionSeparable
  18. 2021-10-08 2021年中式烹调师(初级)新版试题及中式烹调师(初级)免费试题
  19. 求两个数的平均值(三种方法)
  20. python画出e指数函数的图像

热门文章

  1. 《ASCE1885的源码分析》の跨平台互斥对象Mutex封装类
  2. Discuz代码分析——index.php
  3. 日常积累的一些linux和运维的东西 [转]
  4. hive环境搭建提示: java.lang.NoSuchMethodError: com.google.common.base.Preconditions.checkArgument
  5. 学计算机excel就很好吗,零基础学习excel小技巧
  6. 在局域网访问_管理Windows访问凭证,快速访问局域网上的共享资源
  7. 双11大返厂,用Python打造最强告白代码,单身节脱单靠自己
  8. mysql 2003错误 10055_MYSQL无法连接 提示10055错误的解决方法
  9. qt获取窗口的右上角位置_如何获得 Qt窗口部件在主窗口中的位置--确定鼠标是否在某一控件上与在控件上的位置...
  10. node开启子线程_真Node多线程