条款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. USACO Preface Numbering 构造

    一开始看到这道题目的时候,感觉好难 还要算出罗马的规则. 但是仔细一看,数据规模很小, n 只给到3500 看完题目给出了几组样例之后就有感觉了 解题方法就是: n的每个十进制数 转换成相应的罗马数字 ...

  2. HTTP 301 跳转和302跳转的区别

    常用的重定向方式有: 301 redirect, 302 redirect 与 meta fresh: 301 redirect::301代表永久性转移(Permanently Moved),301重 ...

  3. .yml是什么文件

    YAML(IPA: /ˈjæməl/,尾音类似camel骆驼)是一个可读性高,用来表达资料序列的编程语言.YAML参考了其他多种语言,包括:XML.C语言.Python.Perl以及电子邮件格式RFC ...

  4. Java程序猿从笨鸟到菜鸟之(九十二)深入java虚拟机(一)——java虚拟机底层结构具体解释

    本文来自:曹胜欢博客专栏.转载请注明出处:http://blog.csdn.net/csh624366188 在曾经的博客里面,我们介绍了在java领域中大部分的知识点,从最基础的java最基本的语法 ...

  5. 【转】java--final

    1.final数据 许多程序设计语言都有自己的办法告诉编译器某个数据是“常数”.常数主要应用于下述两个方面: (1) 编译期常数,它永远不会改变 (2) 在运行期初始化的一个值,我们不希望它发生变化 ...

  6. Resist the Temptation of the Singleton Pattern

    Resist the Temptation of the Singleton Pattern Sam Saariste THE SiNGLETON PATTERN SOLVES MANY OF YOU ...

  7. stm32智能小车之路之小车启动

           首先.安装完小车后最激动的还是想让他跑动,那么就開始吧.写个简单的程序測试下电机是否正常.打开keil软件新建一个project,详细简历keilproject不会的请百度.或者call ...

  8. 高仿114la网址导航源码完整最新版

    给大家本人我精心模仿的高仿114la网址导航源码,我们都知道114la网址导航的影响力,喜欢的朋友可以下载学习一下.  由于文件较大,没有上传了,下载地址在下面有的. 附源码下载: 114la网站导航 ...

  9. 使用 jackson序列格式化日期

    [1]自定义时间,序列化类 [java] view plaincopy package com.fsti.bm.utils; import java.io.IOException; import ja ...

  10. Oracle数据库top10物理段

    select owner, name, type, mega, tbs   from (select owner,                case                  when ...