• 【effective c++】定制new和delete


    条款49: 了解new-handler的行为

    operator new 和 operator delete只适合用来分配单一对象。array所用的内存由operator new[]分配出来,并由operator delete[] 释放。

    1、了解new-handler的行为

    当operator new无法满足某一内存分配需求时,它会先调用指定的错误处理函数(new-handler),然后抛出异常。可通过set_new_handler函数来设置new-handler。

    namespace std {
    typedef void (*new_handler)();
    new_handler set_new_handler(new_handler p) throw();
    }

    new_handler是个typedef,定义出一个指针指向函数,该函数没有参数也不返回任何东西。

    为当前class提供专属new-handler,只需为它提供自己的set_new_handler和operator new(分配内存)即可。

    class Widget {
    public:
        static std::new_handler set_new_handler(std::new_handler p) throw();
        static void* operator new(std::size_t size) throw(std::bad_alloc);
    private:
        static std::new_handler currentHandler;
    };
    
    std::new_handler Widget::currentHandler = 0;
    
    // 注意此处要返回之前使用的new_handler
    std::new_handler Widget::set_new_handler(std::new_handler p) throw() {
        std::new_handler oldHandler = currentHandler;
        currentHandler = p;
        return oldHandler;
    }
    
    void* Widget::operator new(std::size_t size) throw(std::bad_alloc) {
        NewHandlerHolder h(std::set_new_handler(currentHandler));
        return ::operator new(size);
    }
    
    // 为确保原本的new-handler能够被重新安装回去, 运用资源管理对象
    class NewHandlerHolder {
    public:
        explicit NewHandlerHolder(std::new_handler nh): handler(nh){}
        ~NewHandlerHolder(){
            std::set_new_handler(handler);
        }
    private:
        std::new_handler handler;
        // 防止copy
        NewHandlerHolder(const NewHandlerHolder&);
        NewHandlerHolder& operator=(const NewHandlerHolder&);
    };

    使用nothrow new只能保证operator new不抛出异常,若分配失败则返回NULL,不保证new (std::nothrow) Widget这样的表达式绝不导致异常,因为Widget构造函数中可能继续new内存出现异常

    Widget *p = new (std::nothrow) Widget;


    条款50:了解new和delete的合理替换时机 <pass>

    条款51:编写new和delete时需固守常规

    1、operator new应该内含一个无穷循环, 并在其中尝试分配内存,如果它无法满足内存需求,就该调用new-handler.

    2、operator delete应该在收到null指针时不做任何事

    class Base {
    public:
        static void* operator new(std::size_t size) throw(std::bad_alloc) ;
        static void operator delete(void *rawMemory, std::size_t size) throw();
    };
    
    /*
    operator new成员函数会被derived class继承,而定制内存管理器的一个常见理由是为某特定class的对象
    分配行为提供最优化,即针对Base类设计的operator new其行为可能只为大小刚好为sizeof(Base)的对象而设计
    */
    
    void* Base::operator new(std::size_t size) throw(std::bad_alloc) {
        if (size != sizeof(Base)) {
            return ::operator new(size);
        }
        // 专为Base类定制的行为
        // ...
    }
    
    void Base::operator delete(void *rawMemory, std::size_t size) throw() {
        if (rawMemory == 0) {
            return;
        }
        // 与上述::operator new对应
        if (size != sizeof(Base)) {
            ::operator delete(rawMemory);
            return;
        }
        // 释放rawMemory所指的内存
        return;
    }

    条款52:写了placement new也要写placement delete

    placement new:  多了pMemory参数

    void* operator new(std::size_t size, void* pMemory) throw();

    如果一个带额外参数的operator new 没有带相同额外参数的对应版operator delete,那么当new的内存分配动作需要取消并恢复原样时就没有任何operator delete会被调用,导致内存泄漏。

    placement delete只有在伴随placement new调用而触发的构造函数出现异常时才会被调用,delete某指针时不会导致调用placement delete。

    class Widget {
    public:
        static void* operator new(std::size_t size, std::ostream &logStream) throw (std::bad_alloc);
        static void operator delete(void *pMemory) throw();
        static void operator delete(void *pMemory, std::ostream &logStream) throw();
    };
    
    // 若引发Widget构造函数抛出异常,会自动调用placement delete
    Widget *pw = new (std::cerr) Widget;
    // 调用正常的operator delete
    delete pw;

    缺省情况下c++在global作用域内提供以下形式的operator new:

    void* operator new(std::size_t) throw(std::bad_alloc); // normal new
    void* operator new(std::size_t, void*) throw(); // placement new
    void* operator new(std::size_t, const std::nothrow_t&) throw();

    如果在class内声明任何operator new,会隐藏上述这些标准形式。为避免出现这种情况,可以建立一个Base class,内含所有正常形式的new和delete,凡是想以自定义形式扩充标准形式的客户,可利用继承机制及using 声明式取得标准形式。

  • 相关阅读:
    js动态生成按钮,页面用DIV简单布局
    Maven初学之经验浅谈
    pl/sql注册码
    windows server 2012R2 网络慢的那些事
    sql 优化
    巧用selectKey
    list集合,map集合遍历
    oracle中declare程序块用法
    处理oracle锁表
    关于img标签图片不加载不识别相对路径得解决办法
  • 原文地址:https://www.cnblogs.com/ljygoodgoodstudydaydayup/p/6607580.html
Copyright © 2020-2023  润新知