zoukankan      html  css  js  c++  java
  • Effective C++ 条款52 写了placement new也要写placment delete

    1. placement new和place ment delete指的是正常的operator new和operator delete的重载版本,所谓的正常的operator new和delete,指的是拥有以下正常签名式的版本:

    void* operator new(std::size_t) throw(std::bad_alloc);
    void operator delete(void*) throw();  //global作用域中的正常签名式
    void operator delete(void*,std::size_t) throw(); //class作用域中的正常签名式
    View Code

        由于C++允许对重载的operator new和operator delete添加额外参数,因而具有额外参数的operator和operator delete就是placement new和placement delete.众多placement new版本中比较有用的一个是"接受一个指针指向对象该被构造之处",其主要用途是接受一个指针,然后将其返回以供构造函数在其上构造对象,签名式为:

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

        这个版本的new已被纳入C++标准程序库,使用它需要#include<new>,通常所指的placement new指的就是这一版本.

    2. 对于以下语句:

    Widget* pw1=new Widget;  //Widget是一个类

        共有两个函数被调用:一个是用以分配内存的operator new,另一个是用于构造Widget的Widget default构造函数.

        假设第一个函数调用成功,第二个却抛出异常,那么步骤1所申请的内存必须被释放,否则就是内存泄露.这个任务客户端无法做到,因此由C++运行期系统来完成:运行期系统会调用步骤一所调用的相应operator delete版本.所谓"相应",与具有正常签名式的operator new相应的版本就是具有正常签名式的operator delete(1中所列),而对于placement new,"相应"指的是额外参数相同.

        假设Widget的定义如下:

    class Widget{
    public:
        ...
        static void* operator new(std::size_t size,std::ostream& logStream) throw(std::bad_alloc); //placement operator new
        static void operator delete(void* pMemory,std::size_t size) throw();   //正常operator delete
        ...
    };
    View Code

        如果对于以下语句:

    Widget* pw=new (std::cerr) Widget;

        如果该语句在Widget default构造函数中抛出异常,那么运行期系统有责任取消operator new的分配并恢复旧观,正如以上所言,它需要找到与

    static void* operator new(std::size_t size,std::ostream& logStream) throw(std::bad_alloc);

        相应的operator delete来执行该任务,然而并没有签名式为:

    static void operator delete(std::size_t size,std::ostream& logStream) throw();

    的operator delete.结果就是"如果一个带额外参数的operator new没有'带相同额外参数'的对应版operator delete,那么当new的分配动作需要取消并回复旧观时就没有任何operator delete会被调用",内存泄露也就不可避免.解决方法就是为Widget声明并定义一个与之前带额外参数的operator new对应的operator delete:

    class Widget{
    public:
        ...
        static void* operator new(std::size_t size,std::ostream& logStream) throw(std::bad_alloc); //placement operator new
        static void operator delete(void* pMemory,std::size_t size) throw();   //正常operator delete
        static void operator delete(std::size_t size,std::ostream& logStream) throw(); //对应的operator delete
        ...
    };
    View Code

        这样改变之后,如果以下语句引发Widget构造函数抛出异常:

    Widget* pw=new(std::cerr) Widget;

    对应的placement delete会被调用.

        需要注意的是,只有当抛出异常时,调用的才会是对应的placement delete,如果以上语句执行正常,那么执行:

    delete pw;

    调用的是正常版本的operator delete.

    3. 此外,C++的名称遮掩规则也会导致未预期的后果,假设有一个Base类声明如下:

    class Base{
    public:
        ...
        static void* operator new(std::size_t size,std::ostream& logStream) throw(std::bad_alloc);
        ...
    };
    View Code

        那么由于名称遮掩规则,Base以及其派生类将无法使用global作用域内的operator new:

    //标准库global作用域内的operator new
    void* operator new(std::size_t) throw(std::bad_alloc);
    void* operator new(std::size_t,void*) throw();
    void* operator new(std::size_t,const std::nothrow_t&) throw();
    View Code

        要使用这些形式,只需要令class专属版本调用global版本即可,同时必须注意operator new和operator delete的对应:

    class StandardNewDeleteForms{
    public:
        // normal new/delete
        static void* operator new(std::size_t size) throw(std::bad_alloc){
            return ::operator new(size);
        }
        static void operator delete(void* pMemory) throw(){
            return ::operator delete(pMemory);
        }
        //placement mew/delete
         static void* operator new(std::size_t size,void* ptr) throw(std::bad_alloc){
            return ::operator new(size,ptr);
        }
        static void operator delete(void* pMemory,void* ptr) throw(){
            return ::operator delete(pMemory,ptr);
        }
        //nothrow new/delete
        static void* operator new(std::size_t size,const std::nothrow_t& nt) throw(std::bad_alloc){
            return ::operator new(size,nt);
        }
        static void operator delete(void* pMemory,const std::nothrow_t& nt) throw(){
            return ::operator delete(pMemory);
        }
        ...
    };
    View Code

        凡是想以自定义形式扩充标准形式的客户,可利用继承机制及using声明式取得标准形式:

    class Widget:public StandardNewDeleteForms{
    public:
        using StandardNewDeleteForms::operator new;
        using StandardNewDeleteForms::operator delete;
        static void* operator new(std::size_t size,std::ostream& logStream) throw(std::bad_alloc);
        static void operator delete(void* pMemory,std::ostream& logStream) throw();
        ...
    };
    View Code
  • 相关阅读:
    verilog BRAM 读写
    verilog 语法一 led 翻转
    面试 遇到 问题
    S32K144+UJA1169 (四 ) S32K144 SPI1 功能初始化
    S32K144+UJA1169 ( 三 ) S32K144 SPI1 功能初始化
    S32K144+UJA1169 ( 二 ) S32K144 SPI1 对应的引脚 初始化 为 SPI 功能
    S32K144+UJA1169 ( 一 ) 连接框架+1169 功能 说明
    编译 xboot
    make clean make[1]:sdl2-config:命令未找到
    lwip 内存配置和使用,以及 如何 计算 lwip 使用了多少内存?
  • 原文地址:https://www.cnblogs.com/reasno/p/4803984.html
Copyright © 2011-2022 走看看