条款8 写operator new 和operator delete 时要遵循常规

重写operator new时, 函数提供的行为要和系统缺省的operator new一致:

1)正确的返回值; 2)可用内存不够时调用出错处理函数; 3)处理0字节内存请求的情况; 避免隐藏标准形式的new;

1)如果内存分配请求成功, 返回指向内存的指针, 失败抛出std::bad_alloc异常; 
operator new实际上不止一次尝试分配内存, 每次失败会调用出错处理函数(期望释放别处的内存), 只有在出错处理函数的指针为空的情况下才抛出异常.

Note 按C++标准要求, 在请求分配0字节的内存时, operator new也要返回一个合法指针.

非类成员形式的operator new伪代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
void 
* operator 
new
(
size_t 
size) 
// operator new 还可能有其它参数
{
    
if 
(size == 0) { 
// 处理0 字节请求时,
        
size = 1; 
// 把它当作1 个字节请求来处理
    
}
    
while 
(1) {
        
"分配 size 字节内存;"
        
if 
(分配成功)
            
return 
(指向内存的指针);
        
// 分配不成功,找出当前出错处理函数
        
new_handler globalHandler = set_new_handler(0);
        
set_new_handler(globalHandler);
        
if 
(globalHandler) (*globalHandler)();
        
else 
throw 
std::bad_alloc();
    
}
}

>处理零字节的请求的技巧是把他作为请求一个字节来处理;

>把handler置为0然后再恢复是因为没法直接得到handler的指针, 必须调用set_new_handler;

>operator new包含一个无限循环: while(1), 跳出循环的条件是内存分配成功或出错处理函数完成事件中的一种: 
得到可用内存; 安装了新的new-handler; 卸除了new-handler; 抛出bad_alloc类型的异常; 返回失败; 所以new-hander必须做到其中一件事, 否则循环无法结束;

operator new经常会被子类继承, 引出复杂度; 大多数指针对类所写的operator new只为特定的类设计的, 不是为其他类或子类设计的;
对于一个类X的operator new来说, 函数内部的行为在涉及到对象的大小时, 都是sizeof(X). 但由于存在继承, 基类中的operator new可能被调用给子类对象分配内存;

1
2
3
4
5
6
7
8
class 
Base {
    
public
:
    
static 
void 
* operator 
new
(
size_t 
size);
...
};
class 
Derived: 
public 
Base 
// Derived 类没有声明operator new
{ ... }; 
//
Derived *p = 
new 
Derived; 
// 调用Base::operator new

>如果Base类的operator new不想处理这种情况, 简单的方法是把内存分配请求转给标准operator new来处理:

1
2
3
4
5
6
7
void 
* Base::operator 
new
(
size_t 
size)
{
    
if 
(size != 
sizeof
(Base)) 
// 如果数量“错误”,让标准operator new
        
return 
::operator 
new
(size); 
// 去处理这个请求
//
... 
// 否则处理这个请求
}

>size != sizeof(Base)处理了size等于零的情况: C++标准规定独立的freestanding类的大小都是非零值; 即使Base没有成员, sizeof(Base)也总是非0; (非独立的类sizeof可能为零) [嵌套类??] size为零时, 请求就会转到::operator new来处理;

如果想控制基于类的数组的内存分配, 必须实现operator new的数组形式: operator new[]("数组new");
写operator new[]时, 面对的是"原始"内存, 不能对数组里还不存在的对象进行操作; 还不知道数组对象的个数和大小;
基类的operator new[]会通过继承的方式被用来为子类对象的数组分配内存, 但子类对象一般比基类的大;
Base::operator new平[]里的每个对象大小不一定都是sizeof(Base), 数组对象的数量不一定就是 (请求字节数)/sizeof(Base);

对于operator delete和operator delete[], 要记住C++保证删除空指针总是安全的;

1
2
3
4
5
6
7
void 
operator 
delete
(
void 
*rawMemory)
{
    
if 
(rawMemory == 0)
        
return
; file:
//如果指针为空,返回
    
//释放 rawMemory指向的内存;
    
return
;
}

假设类的operator new将"错误"大小分配请求转给::operator new, 那么必须将"错误"大小的删除请求转给::operator delete;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class 
Base { 
// 和前面一样,只是这里声明了operator delete
public
:
    
static 
void 
* operator 
new
(
size_t 
size);
    
static 
void 
operator 
delete
(
void 
*rawMemory, 
size_t 
size);
...
};
void 
Base::operator 
delete
(
void 
*rawMemory, 
size_t 
size)
{
    
if 
(rawMemory == 0) 
// 检查空指针
        
return
;
    
if 
(size != 
sizeof
(Base))
    

// 如果size"错误",让标准operator 来处理请求
        
::operator 
delete
(rawMemory);
        
return
;
    
}
    
"释放指向 rawMemory的内存;"
    
return
;
}

必须遵守operator new和operator delete的规定; 需要内存分配程序支持new-handler函数, 并正确处理零内存请求;

条款9 避免隐藏标准形式的new

内部范围声明的名称会隐藏掉外部范围的相同名称, 所以对于在类的内部和全局声明的同名函数f来说, 类成员函数会隐藏掉全局函数;

1
2
3
4
5
6
7
8
void 
f(); 
// 全局函数
class 
X {
public
:
    
void 
f(); 
// 成员函数
};
X x;
f(); 
// 调用 f
x.f(); 
// 调用 X::f

>调用全局函数和成员函数时采用的是不同的语法形式;

但是如果在类里增加一个带多个参数的operator new函数:

1
2
3
4
5
6
7
8
9
10
class 
X {
public
:
    
void 
f();
// operator new 的参数指定一个 new-hander(new 的出错处理)函数
    
static 
void 
* operator 
new
(
size_t 
size, new_handler p);
};
void 
specialErrorHandler(); 
// 定义在别的地方
 
X *px1 = 
new 
(specialErrorHandler) X; 
// 调用X::operator new
X *px2 = 
new 
X; 
//错误

>类里定义了"operator new"函数后, 会阻止对标准new的访问;

Solution 1)在类里写一个支持标准new调用方式的operator new, 和标准new做同样的事情;

1
2
3
4
5
6
7
8
9
class 
X {
public
:
    
void 
f();
    
static 
void 
* operator 
new
(
size_t 
size, new_handler p);
    
static 
void 
* operator 
new
(
size_t 
size) { 
return 
::operator 
new
(size); }
};
 
X *px1 = 
new 
(specialErrorHandler) X; 
// 调用 X::operator new(size_t, new_handler)
X* px2 = 
new 
X; 
// 调用 X::operator new(size_t)

>使用内联实现;

Solution 2)为每一增加到operator new的参数提供缺省值;

1
2
3
4
5
6
7
class 
X {
public
:
    
void 
f();
    
static 
void 
* operator 
new
(
size_t 
size, new_handler p = 0); 
// p 缺省值为0
};
X *px1 = 
new 
(specialErrorHandler) X; 
// 正确
X* px2 = 
new 
X; 
// 也正确

>以后想对"标准"形式的new定制新的功能, 只需重写这函数;

Effective C++ 第二版 8) 写operator new 和operator delete 9) 避免隐藏标准形式的new的更多相关文章

  1. Effective C++ 第二版 10) 写operator delete

    条款10 写了operator new就要同时写operator delete 写operator new和operator delete是为了提高效率; default的operator new和o ...

  2. Effective Java 第二版 Enum

    /** * Effective Java 第二版 * 第30条:用enum代替int常量 */ import java.util.HashMap;import java.util.Map; publi ...

  3. Effective C++ 第二版 17)operator=检查自己 18)接口完整 19)成员和友元函数

    条款17 在operator=中检查给自己赋值的情况 1 2 3 class  X { ... }; X a; a = a;  // a 赋值给自己 >赋值给自己make no sense, 但 ...

  4. Effective C++ 第二版 5)new和delete形式 6) 析构函数里的delete

    内存管理 1)正确得到: 正确调用内存分配和释放程序; 2)有效使用: 写特定版本的内存分配和释放程序; C中用mallco分配的内存没有用free返回, 就会产生内存泄漏, C++中则是new和de ...

  5. Effective C++ 第二版 31)局部对象引用和函数内new的指针 32)推迟变量定义

    条款31 千万不要返回局部对象的引用, 不要返回函数内部用new初始化的指针的引用 第一种情况: 返回局部对象的引用; 局部对象--仅仅是局部的, 在定义时创建, 在离开生命空间时被销毁; 所谓生命空 ...

  6. 《Effective Java第二版》总结

    第1条:考虑用静态工厂方法代替构造器 通常我们会使用 构造方法 来实例化一个对象,例如: // 对象定义 public class Student{ // 姓名 private String name ...

  7. Effective C++ 第二版 40)分层 41)继承和模板 42)私有继承

    条款40 通过分层来体现"有一个"或"用...来实现" 使某个类的对象成为另一个类的数据成员, 实现将一个类构筑在另一个类之上, 这个过程称为 分层Layeri ...

  8. Effective C++ 第二版 1)const和inline 2)iostream

    条款1 尽量用const和inline而不用#define >"尽量用编译器而不用预处理" Ex. #define ASPECT_R 1.653    编译器永远不会看到AS ...

  9. 《Effective Java 第二版》读书笔记

    想成为更优秀,更高效程序员,请阅读此书.总计78个条目,每个对应一个规则. 第二章 创建和销毁对象 一,考虑用静态工厂方法代替构造器 二, 遇到多个构造器参数时要考虑用builder模式 /** * ...

随机推荐

  1. Qt对话框_模态/非模态

    对话框在Qt GUI应用程序中有着广泛的用途,对话框有模态.非模态两种情况. 对于参数选择的对话框,一般用模态对话框:对于显示或查看某些内容的对话框,一般用非模态对话框. 对话框类QDialog,官方 ...

  2. ONOS系统架构演进,实现高可用性解决方案

    上一篇文章<ONOS高可用性和可扩展性实现初探>讲到了ONOS系统架构在高可用.可扩展方面技术概况,提到了系统在分布式集群中怎样保证数据的一致性.在数据终于一致性方面,ONOS採用了Gos ...

  3. 安装Devstack的DNS问题

    所谓的OpenStack一键安装,省去了敲键盘的麻烦,但是卡在中间出了问题也是比较尴尬的 在公司内安装经常会出现卡在下载软件的地方,有时候还会出错 trick就是换一个US的dns,比如8.8.8.8

  4. SQL SERVER 2008R2sp1配置Database Mail –用SQL 数据库发邮件

    步骤1)创建配置文件和帐户 看图片吧,挺简单的: 中间略过的一些步骤,就点下一步即可. 下面我们测试一下: Step 2)配置邮件: 在完成账户和配置文件创建之后,我们需要配置Database Mai ...

  5. TOMCAT的域名配置

    链接地址:http://blog.163.com/skk1987@126/blog/static/29303413201051383548377/ 现在很多的公司的网站都是用tomcat作为应用服务区 ...

  6. 操作VCF卡片信息的第三方jar包:ez-vcard

    ez-vcard https://github.com/mangstadt/ez-vcard 目前最新的版本已经更新到0.9.8 起初使用该jar包的时候,是0.9.3,当时遇到一个很尴尬的问题, 就 ...

  7. keepalive的 nopreempt 非抢占

    通常如果master服务死掉后backup会变成master,但是当master服务又好了的时候 master此时会抢占VIP,这样就会发生两次切换对业务繁忙的网站来说是不好的.所以我们要在配置文件加 ...

  8. 基于visual Studio2013解决C语言竞赛题之0704字符串长度

     题目

  9. 找出并解决 JavaScript 和 Dojo 引起的浏览器内存泄露问题

    简介: 如果大量使用 JavaScript 和 Ajax 技术开发 Web 2.0 应用程序,您很有可能会遇到浏览器的内存泄漏问题.如果您有一个单页应用程序或者一个页面要处理很多 UI 操作,问题可能 ...

  10. Java Socket实现HTTP客户端来理解Session和Cookie的区别和联系

    HTTP协议本身是无状态的,即使是同一台电脑同一个浏览器打开同一个页面两次,服务器不知道这两次请求是同一个客户端发送过来的,两次请求是完全独立的.例如,第一次请求时已经登录了,第二次再请求服务器会“忘 ...