zoukankan      html  css  js  c++  java
  • Item 51:写new和delete时请遵循惯例

    Item 51: Adhere to convention when writing new and delete.

    Item 50介绍了怎样自己定义newdelete但没有解释你必须遵循的惯例。 这些惯例中有些并不直观,所以你须要记住它们!

    • operator new须要无限循环地获取资源。假设没能获取则调用”new handler”。不存在”new handler”时应该抛出异常。
    • operator new应该处理size == 0的情况。
    • operator delete应该兼容空指针。
    • operator new/delete作为成员函数应该处理size > sizeof(Base)的情况(由于继承的存在)。

    外部operator new

    Item 49指出了怎样将operator new重载为类的成员函数,在此我们先看看怎样实现一个外部(非成员函数)的operator new: operator new应当有正确的返回值。在内存不足时应当调用”new handler”,请求申请大小为0的内存时也能够正常运行。避免隐藏全局的(”normal form”)new

    • 给出返回值非常easy。

      当内存足够时。返回申请到的内存地址;当内存不足时。依据Item 49描写叙述的规则返回空或者抛出bad_alloc异常。

    • 每次失败时调用”new handler”,并反复申请内存却不太easy。仅仅有当”new handler”为空时才应抛出异常。
    • 申请大小为零时也应返回合法的指针。同意申请大小为零的空间确实会给编程带来方便。

    考虑到上述目标,一个非成员函数的operator new大致实现例如以下:

    void * operator new(std::size_t size) throw(std::bad_alloc){
        if(size == 0) size = 1;
        while(true){
            // 尝试申请
            void *p = malloc(size);
    
            // 申请成功
            if(p) return p;
    
            // 申请失败,获得new handler
            new_handler h = set_new_handler(0);
            set_new_handler(h);
    
            if(h) (*h)();
            else throw bad_alloc();
        }
    }
    
    • size == 0时申请大小为1看起来不太合适,但它很easy并且能正常工作。况且你不会常常申请大小为0的空间吧?
    • 两次set_new_handler调用先把全局”new handler”设置为空再设置回来,这是由于无法直接获取”new handler”,多线程环境下这里一定须要锁。
    • while(true)意味着这可能是一个死循环。所以Item 49提到,”new handler”要么释放很多其它内存、要么安装一个新的”new handler”,假设你实现了一个没用的”new handler”这里就是死循环了。

    成员operator new

    重载operator new为成员函数一般是为了对某个特定的类进行动态内存管理的优化,而不是用来给它的子类用的。 由于在实现Base::operator new()时,是基于对象大小为sizeof(Base)来进行内存管理优化的。

    当然。有些情况你写的Base::operator new是通用于整个class及其子类的,这时这一条规则不适用。

    class Base{
    public:
        static void* operator new(std::size_t size) throw(std::bad_alloc);
    };
    class Derived: public Base{...};
    
    Derived *p = new Derived;       // 调用了 Base::operator new !

    子类继承Base::operator new()之后,由于当前对象不再是如果的大小。该方法不再适合管理当前对象的内存了。

    能够在Base::operator new中推断參数size。当大小不为sizeof(Base)时调用全局的new

    void *Base::operator new(std::size_t size) throw(std::bad_alloc){
        if(size != sizeof(Base)) return ::operator new(size);
        ...
    }
    

    上面的代码没有检查size == 0。这是C++奇妙的地方,大小为0的独立对象会被插入一个char(见Item 39)。 所以sizeof(Base)永远不会是0,所以size == 0的情况交给::operator new(size)去处理了。

    这里提一下operator new[]。它和operator new具有相同的參数和返回值, 要注意的是你不要如果当中有几个对象。以及每一个对象的大小是多少,所以不要操作这些还不存在的对象。

    由于:

    1. 你不知道对象大小是什么。上面也提到了当继承发生时size不一定等于sizeof(Base)

    2. size实參的值可能大于这些对象的大小之和。由于Item 16中提到。数组的大小可能也须要存储。

    外部operator delete

    相比于new,实现delete的规则要简单非常多。唯一须要注意的是C++保证了delete一个NULL总是安全的,你尊重该惯例就可以。

    相同地,先实现一个外部(非成员)的delete

    void operator delete(void *rawMem) throw(){
        if(rawMem == 0) return; 
        // 释放内存
    }
    

    成员operator delete

    成员函数的delete也非常easy,但要注意假设你的new转发了其它size的申请,那么delete也应该转发其它size的申请。

    class Base{
    public:
        static void * operator new(std::size_t size) throw(std::bad_alloc);
        static void operator delete(void *rawMem, std::size_t size) throw();
    };
    void Base::operator delete(void *rawMem, std::size_t size) throw(){
        if(rawMem == 0) return;     // 检查空指针
        if(size != sizeof(Base)){
            ::operator delete(rawMem);
        }
        // 释放内存
    }
    

    注意上面的检查的是rawMem为空,size是不会为空的。

    事实上size实參的值是通过调用者的类型来推导的(假设没有虚析构函数的话):

    Base *p = new Derived;  // 如果Base::~Base不是虚函数
    delete p;               // 传入`delete(void *rawMem, std::size_t size)`的`size == sizeof(Base)`。

    假设Base::~Base()声明为virtual,则上述size就是正确的sizeof(Derived)。 这也是为什么Item 7指出析构函数一定要声明virtual

  • 相关阅读:
    关于App_Offline.htm的应用实例(及CIM_DataFile的用法)注意Windows下
    Office2007多个文档打开时,开启多个窗口(独立进程)
    Asp.Net环境下web Pages,web Forms 及MVC的优越及缺点
    批量生成表Create SQL 示例 Generate SQL Create Scripts for existing tables with Query
    Different between datetime and timestamp, and its setting
    SqlConnection ,SqlTransaction,SqlCommand的常用法
    ASP.NET下从Server端下载文件到Client端C#
    C#中Remote文件复制简例子
    DOS BAT用法简例子
    改善SQL Procedure性能的几点方法
  • 原文地址:https://www.cnblogs.com/clnchanpin/p/7222410.html
Copyright © 2011-2022 走看看