Effective cpp 读书笔记10

定制new和delete

49.了解new-handler的行为

  1. set_new_handler允许客户指定一个函数,在内存分配无法获得满足时被调用

  2. Nothrow new是一个颇为局限的工具,因为它只适用于内存分配;后续的构造函数调用还是可能抛出异常

    • new_handler:当operator new抛出异常以反映一个未获得满足的内存需求之前,它会先调用一个客户指定的错误处理函数,new_handler;这是一个typedef,定义一个指针指向函数。下面是它的基本形式,其中,set_new_handler也是一个指针,指向new无法分配足够内存时该被调用的函数
    • 一个良好的new_handler需要满足:让更多内存可被使用;安装另一个new_handler;卸载当前的new_handler;抛出bad_alloc异常
    • C++不支持class专属的new_handler,但可以自己实现,只要实现静态的set_new_handler和静态的operator new即可
    • Nothrow new是老式的形式,只是说new出错了不抛出异常,但是一般new都会带有class的构造,构造出错,照样抛出异常,照样可能泄露
namespace std
{
    void (*new_handler)();
    void set_new_handler(new_handler) throw();
}

50.了解new和delete的合理替换时机

  1. 为了检测运行错误:可能存在内存泄漏、数据overruns(写入点在分配区块尾端之后)、数据underrun(写入点在分配区块起始之前)

  2. 为了强化效能:自带的new和delete因为要考虑过多的问题,有可能导致内部碎片

  3. 为了收集使用上的统计数据:想自己决定分配和归还的次序,比如FIFO、最近最少使用等

  4. 为了增加分配和归还速度:泛用型的分配器往往比定制型分配器慢

  5. 为了降低缺省内存管理器带来的空间额外开销:泛用型的分配器往往比定制型分配器消耗更多内存,因为会为每个分配去增加开销。对小型对象开发自己的分配器

  6. 为了弥补缺省分配器中的非最佳齐位:编译器自带的new不一定能保证

  7. 为了将相关对象成簇集中:某些对象会被一起使用,为他们创建一个heap,减少内存页消耗,加快速度

  8. 为了获得非传统的行为


51.编写new和delete时需固守常规

  1. operator new应该内含一个无穷循环。并在其中尝试分配内存,如果它无法满足内存需求,就应该调用new_handler。它也应该有能力处理0 bytes申请。Class专属版本则还应该处理“比正确大小更大的(错误)申请”

  2. operator delete应该在收到null指针时不做任何事。Class专属版本则还应该处理“比正确大小更大的(错误)申请”

    • operator new内含有无穷循环(P252),会一直尝试分配内存,知道成功或者抛出异常
    • C++规定,如果申请0bytes,也需要返回合法指针,默认指针指向的大小为1
    • Class可能存在继承关系,base class的new专属版本应该只是针对base class,它的派生类应该自己定义new。但是,base class的new需要能够处理错误大小的申请

52.写出placement new也要写placement delete

  1. 当你写一个placement operator new,请确定也写出对应的placement operator delete。如果没有,你的程序可能会发生隐微而时断时续的内存泄漏

  2. 当你声明placement new和placement delete,请确定不要无意识地遮掩它们的正常版本

    • 当你调用了new,会执行内存分配,同时执行构造函数。如果在构造的时候出错,只能由C++运行时系统delete对应的内存。而C++调用的delete会是对应new的delete
    • 运行期系统寻找“参数个数和类型都与operator new相同”的某个operator delete。如果找到,那就是它的调用对象
    • default版本的new和delete
      – void* operator new(std::size_t) throw (std::bad_alloc);
      – void operator delete (void* rawMemory) throw(); // global作用域中的正常签名式
      – void operator delete (void* rawMemory,std::size_t size) throw (); //class 作用域中典型的签名式

    • 典型placement new:void* operator new (std::size_t, void* pMemory) throw ()

    • 典型placement delete:void operator delete (std::size_t, void* pMemory) throw()
    • placement delete只有在“伴随placement new调用而触发的构造函数”出现异常时才会被调用,对着一个指针施行delete绝对不会导致调用placement delete。也就是说,即使你的指针是placement new产生的,用户调用delete操作,也是正常的delete
    • 如果你在class内声明任何operator new,它会遮掩标准的new。意思是用户没法调用正常的new了,所以你还必须重载这些正常的new,不过很简单,只需要在对应函数内调用正常的new即可。delete也一样
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值