我们可以量身定制 operator new 和 operator delete 用于某个类类型,而不是必须使用标准版的 operator new 和 operator delete。

注意:我们不可以对 new  和 delete 操作符做什么,它们的行为是固定的。但是我们可以改变 它们所调用的 operator new 和 operator delete 。做这件事的最佳方式是为类声明 operator new 和 operator delete成员函数:

class Handle
{
public:
//...
void * operator new (size_t);
void operator delete(void *);
};
//...
Handle * h = new Handle;//使用Handle::operator new
//...
delete h;//使用Handle::operator delete

在一个new表达式中分配一个类型为 Handle 的对象时,编译器首先会在Handle 的作用域内查找一个 operator new ,如果没有找到,它将会使用全局作用域中的 operator new。 operator delete 情况是类似的。因此通常来说,如果定义一个operator new ,最好同时也定义一个成员 operator delete   ,反之亦然。

成员 operator new 和 operator delete 是静态成员函数,这是有道理的。更确切的说,成员 operator new   和 operator delete 之所以是静态成员,是因为前者被调用于类对象构造之前,后者被调用与类对象析构之后,在这两种情况下,对象均不处于有效状态,因此也就无 this 指针可用。我们可以回想起静态成员函数没有 this 指针。由于这个函数仅仅负责获取和释放对象的存储区,因此它们用不着 this 指针。 它们可以被派生类继承:

class MyHandle:public Handle
{
public:
//...
};
//...
MyHandle * myH = new MyHandle; //使用Handle::operator new
//...
delete myH; //使用Handle::operator delete

当然,如果 MyHandle 已经声明了它自己的 operator new 和 operator delete 成员,那么编译器将在查找期间首先发现并采用它们,而不再使用从基类 Handle 继承来的那个版本。

如果在基类的中了成员 operator new  和 operator delete ,要确保基类析构函数是虚拟的:

class Handle
{
public:
//...
virtual ~Handle();
void * operator new (size_t);
void operator delete(void *);
}; class MyHandle:public Handle
{
public:
//...
void * operator new (size_t);
void operator delete(void *,size_t);//注意第二个参数
}; MyHandle * myH = new MyHandle; //使用MyHandle::operator new
//...
delete myH; //使用 MyHandle::operator delete

对于

void operator delete(void * ,size_t);而言,第二个类型为size_t的参数有编译器自动初始化为第一个参数所致对象的大小,以字节为单位。

如果基类析构函数不是虚拟的,那么通过一个基类指针来删除一个派生类对象结果就是未定义的!一个可能简单地实现(并且很可能不是正确地)调用 Handle::operator delete 而不是MyHandle::operator delete,但这样做发生任何事情都是可能的。还要注意的是,我们使用了一个带有两个参数的operator delete 而不是普通的单参数版本的operator delete。不过对于期望派生类来继承其operator delete 实现的基类而言,这不过是另一个“普通”版本的成员 operator delete 而已。第二参数用于保存正在被删除的对象大小,这种信息在实现自定义内存管理时往往很有用。

一个常见的误解是以为使用new和delete 操作符就意味着使用堆(或自由存储区)内存,其实并非如此。使用new 操作符的唯一暗示是名为 operator new 的函数将被调用,且该函数返回一个指向某块内存饿指针。没错,标准、全局的 operator new 和 operator delete 的确是从堆上分配内存,但是成员 operator new 和 operator delete可以做它自己想做的任何事情。对于分配的内存到底从哪来没有任何限制:它可能来自于一个特殊的堆,也可能来自一个静态分配的块,也可能来自一个标准的容器内部,也可能来自某个函数范围的局部存储区。要说对于这个内存从南来确实存在什么限制因素的话,那就是你创造力和判断力了。例如,Handle对象可以从一个静态块 (static lock) 上进行分配,如下:

struct rep
{
enum{ max = };
static rep * free; //“自由”列表 (freeList)的头部
static int num_used; //被使用的槽位(slot)数目
union
{
char store[sizeof(Handle)];
rep * next;
};
};
int rep::num_used =;
rep* rep::free = NULL;
static rep mem[ rep::max ]; //静态存储块e
void * Handle::operator new(size_t size)
{
if(rep::free) //如果freelist上有一些东西
{
rep * tmp = rep::free; //从freeList取出
rep::free = rep::free->next;
return tmp;
}
else if( rep::num_used < rep::max )//如果没有剩余的槽位
{
return &mem[ rep::num_used++ ]; //返回未被使用的槽位
}
else //否则,我们现在所处的状况是···
throw std::bad_alloc();//没有可用内存了
}
void Handle::operator delete(void * p)
{
static_cast<rep*>(p)->next = rep::free;
rep::free = static_cast<rep *>(p);
}

对于实现的一个“产品质量”的版本更要小心处理内存不足的情况,应该提供更强健的处理,并且要处理Handle的派生类类型以及Handle数组的情况,等等。不过这段简单的代码已经表明new 和 delete 不必非使用堆内存不可。

此章节代码,我上机测试过,没有问题

啊 。。。好东西!!!好文章!!!受益颇深。。。敲的我好累

2018.3.23我最棒

特定于类的内存管理---《C++必知必会》 条款36的更多相关文章

  1. 《C++必知必会》学习笔记

    转载:http://dsqiu.iteye.com/blog/1734640 条款一 数据抽象 抽象数据设计遵循步骤:(1)为类型取一个描述性的名字.(2)列出类型所能执行的操作,不要忘了初始化(构造 ...

  2. OC5_复合类的内存管理

    // // Person.h // OC5_复合类的内存管理 // // Created by zhangxueming on 15/6/18. // Copyright (c) 2015年 zhan ...

  3. Mat 类的内存管理

    使用 Mat 类,内存管理变得简单,不再像使用 IplImage 那样需要自己申请和释放内存.虽然不了解 Mat 的内存管理机制,也无碍于 Mat 类的使用,但是如果清楚了解 Mat 的内存管理,会更 ...

  4. 《MySQL必知必会》整理

    目录 第1章 了解数据库 1.1 数据库基础 1.1.1 什么是数据库 1.1.2 表 1.1.3 列和数据类型 1.1.4 行 1.1.5 主键 1.2 什么是SQL 第2章 MySQL简介 2.1 ...

  5. 第3节:Java基础 - 必知必会(上)

    第3节:Java基础 - 必知必会(上) 本篇是基础篇的第一小节,我们从最基础的java知识点开始学习.本节涉及的知识点包括面向对象的三大特征:封装,继承和多态,并且对常见且容易混淆的重要概念覆盖和重 ...

  6. 【MySQL 基础】MySQL必知必会

    MySQL必知必会 简介 <MySQL必知必会>的学习笔记和总结. 书籍链接 了解SQL 数据库基础 什么是数据库 数据库(database):保存有组织的数据的容器(通常是一个文 件或一 ...

  7. 必知必会之 Java

    必知必会之 Java 目录 不定期更新中-- 基础知识 数据计量单位 面向对象三大特性 基础数据类型 注释格式 访问修饰符 运算符 算数运算符 关系运算符 位运算符 逻辑运算符 赋值运算符 三目表达式 ...

  8. mysql必知必会

    春节放假没事,找了本电子书mysql必知必会敲了下.用的工具是有道笔记的markdown文档类型. 下面是根据大纲已经敲完的章节,可复制到有道笔记的查看,更美观. # 第一章 了解SQL## 什么是S ...

  9. MySQL使用和操作总结(《MySQL必知必会》读书笔记)

    简介 MySQL是一种DBMS,即它是一种数据库软件.DBMS可分为两类:一类是基于共享文件系统的DBMS,另一类是基于客户机——服务器的DBMS.前者用于桌面用途,通常不用于高端或更关键应用. My ...

随机推荐

  1. Android开发之程序猿必需要懂得Android的重要设计理念2(5.20更新版)

    上篇文章介绍了Android开发的设计理念的一部分,并没有得到博友们的多大认可,仅仅看到了一位博友在以下留言期待下一篇文章的发表,为了这小小的唯一支持.我决定继续把后面的8个要点介绍一下,自己也潜心反 ...

  2. python中模块,包,库

    模块:就是.py文件,里面定义了一些函数和变量,需要的时候就可以导入这些模块. 包:在模块之上的概念,为了方便管理而将文件进行打包.包目录下第一个文件便是 __init__.py,然后是一些模块文件和 ...

  3. remove()

    remove() 用于从列表中删除指定的元素 In [35]: l = ['a', 'b', 'c'] In [36]: l.remove('b') In [37]: l Out[37]: ['a', ...

  4. json 数据分析

    /* 健一健康头条 */ try { String url = "http://www.j1health.com/j1api.php/index/getJ1healthHotLists&qu ...

  5. HttpPost导包遇到的问题

    直接在当前项目 build.gradle文件修改如下 android { useLibrary 'org.apache.http.legacy' compileSdkVersion 24 buildT ...

  6. NUC970烧录文件系统

    燒錄U-Boot依照下列步驟將編譯完成的U-Boot燒錄至NAND Flash/SPI Flash/eMMC 中.U-Boot的編譯方法請參考4.3章節.3.11.1 燒錄所需檔案4. u-boot. ...

  7. 关于MultiByteToWideChar与WideCharToMultiByte代码测试(宽字符与多字节字符的转换)以及字符串的转换代码测试

    #pragma once #include <stdio.h> //getchar() #include <tchar.h> #include <stdlib.h> ...

  8. 【golang】 go语言之环境搭建+ 第一个go语言程序

    1 下载:http://www.golangtc.com/download (以win为例) 2 安装步骤: 安装包: go1.8.windows-amd64.msi 双击进行安装 创建工作目录 :D ...

  9. 小程序 - API 踩坑记录(更新中...)

    API 小程序API结构导览图: 声明: 请尊重博客园原创精神,转载或使用图片请注明: 博主:xing.org1^ 出处:http://www.cnblogs.com/padding1015/

  10. hibernate实现有两种配置,xml配置与注释配置。<转>

    <注意:在配置时hibernate的下载的版本一定确保正确,因为不同版本导入的jar包可能不一样,所以会导致出现一些错误> hibernate实现有两种配置,xml配置与注释配置. (1) ...