Effective C++第八章-new和delete

了解new-handle的行为

当operator new抛出异常以反映一个未获满足的内存需求之前,它会先调用一个客户指定的错误处理函数,一个所谓的new_handle。(new真正做的事情稍微更复杂些)为了指定该函数,客户必须要调用set_new_handler,那是声明于< new >的一个标准程序库函数:

namespace std{
    typedef void(* new_handler)();//new_handler是一个typedef,定义一个指针指向函数,该函数没有参数也不返回任何东西。
    new_handler set_new_handler(new_handler p) throw();//set_new_handler则是获得一个new_handler并返回一个new_handler的函数。参数指针指向operator new 无法分配足够内存时该被调用的函数,返回的指针指向set_new_handler被调用前正在执行的那个new_handler函数。
}
void MyOutOfMemory()
{
    cout << "Out of memory error!" << endl;
    abort();
}
int main()
{
    set_new_handler(MyOutOfMemory);
    int *verybigmemory = new int[0x1fffffff];
    ...
}

定义于< new> 中的nothrow常量–该常量用来作为operator new和operator new[]的参数,说明这两个函数分配内存失败时不抛出异常,而是返回一个null指针。

class widget{...};
widget* pw1 = new widget;//如果分配失败,抛出bad_alloc
if(pw1 == 0 )..//该测试一定失败
widget* pw2 = new (std::nothrow) widget;//如果分配失败,返回0
if(pw2 == 0 )...//该测试可能成功

PS:< new >头文件有:

  • Functions

    • operator new

    • operator new[]

    • operator delete

    • operator delete[]

    • set_new_handler

    • get_new_handler

  • Types(类型)

    • nothrow_t

    • new_handler

    • bad_alloc

    • bad_array_new_length

  • Constants(常量)

    • nothrow

编译器自带的new和delete PK 自制版本的new和delete

自制版本的new和delete的好处:

  1. 用来检测运用上的错误
  2. 效能胜过缺省版本
  3. 自制版本的new和delete可轻松收集下列信息:在定制性new和定制性delete之前,理当先收集你的软件如何使用其动态内存,分配区块的大小分布如何?寿命分布如何?倾向于FIFO(先进先出)还是LIFO次序或随机次序来分配和归还?运用型态是否随时间改变,也就是在不同的执行阶段有不同的分配/归还型态吗?任何时刻所使用的最大动态分配量是多少?

齐位(alignment)

许多计算机体系结构要求特定的类型必须放在特定的内存地址上,例如它可能会要求指针的地址必须是4倍数或doubles的地址必须是8倍数。如果没有奉行这个约束条件,可能导致运行期硬件异常。

C++要求所有operator new返回的指针都有适当的对齐(取决于数据类型)。

编写new和delete需要遵守的规则

  • new

    operator new 的返回值,如果有能力供应客户申请的内存,就返回一个指针指向那块内存。如果没有那个能力,就调用new_handle函数,该函数也许能做某些动作将某些内存释放出来,只有当指向该函数的指针是null,operator new才会抛出一个bad_alloc异常。

    operator new成员函数会被derived class继承,但是,写定制型内存管理器的一个最常见理由是为针对某特定class对象分配行为提供最优化,却不是为该class的任何derived class。也就是说,针对class X设计的operator new,其行为很典型地只为大小刚好为sizeof(X)而设计。处理该问题的最佳做法就是:改采用标准operator new,例如

    void* Base::operator new(std::size_t size) throw(std::bad_alloc)
    {
    if(size != sizeof(Base))
        return ::operator new(size);//采用标准operator new
    ...
    }
  • delete

    • 保证”删除null指针”永远安全
    • 如果class专属的operator new将大小有误的分配行为转交::operator new 执行,则必须将大小有误的删除行为转交::operator delete执行。
    void* Base::operator delete(void* rawmemory,std::size_t size) throw()
    {
    if(rawmemory== 0 ) return;//保证"删除null指针"永远安全
    if(size != sizeof(Base)){
        ::operator delete(rawmemory);//采用标准operator delete
        return;
    }
    归还rawmemory所指的内存
    return;
    }

placement new和placement delete

C++在全局作用域内提供以下三种operator new:

throwingvoid* operator new (std::size_t size);
nothrowvoid* operator new (std::size_t size, const std::nothrow_t& nothrow_value) noexcept;
placementvoid* operator new (std::size_t size, void* ptr) noexcept;

和以下三种operator delete:

ordinaryvoid operator delete (void* ptr) noexcept;
nothrowvoid operator delete (void* ptr, const std::nothrow_t& nothrow_constant) noexcept;
placementvoid operator delete (void* ptr, void* voidptr2) noexcept;

PS:

  1. C++11中的noexcept大致等同于C++98版的throw();都表示函数不抛出任何异常。
  2. operator new详细介绍
  3. operator delete详细介绍

widget* pw = new widget时,共有两个函数被调用:一个是用以分配内存的operator new,一个是widget的default构造函数。

假设第一个函数调用成功,第二个函数抛出异常,则运行期系统有责任寻找并调用operator new的相应operator delete版本。

需注意的是,如果找不到与之对应的delete,运行期系统会什么都不做,导致内存泄漏。

因此,若定制new和delete请成对,一个带额外参数的operator new最好有带相同额外参数的对应版operator delete。例如:

class widget{
public:
    //placement new 
    static void* operator new(std::size_t size, std::ostream& logstream) throw(std::bad_alloc);
    //ordinary delete
    static void* operator delete(void* pmemory) throw();
    //placement delete
    static void* operator delete(void* pmemory,std::ostream& logstream) throw();
};

对所有与placement new相关的内存泄漏,我们必须同时提供一个正常的operator delete(用于构造期间无任何异常被抛出)和一个带相同额外参数的placement版本(用于构造期间有异常被抛出)。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值