Effective C++ 条款52

写了placement new也要写placement delete

本文主要内容是对placement new 和 placement delete的介绍,以及在什么情况下使用placement new和placement delete。

对于语句Widget* pw=new Widget;来说,该语句做了两件事情,第一件事情是申请了内存区域;第二件事情是在该内存区上进行对象的构造,即调用构造函数。我们设想其中一种执行情况,当第一件事情完成,而第二件事情出现异常,那么我们应该怎么去处理,很显然我们需要将这部分内存还原回去,于是如何还原回去成了我们需要解决的问题。

这个时候,客户没有能力去归还内存,因为如果Widget构造函数抛出异常,那么pw尚未被赋值,客户手中的指针还没有指向开辟的内存。释放内存的任务落到了C++运行期系统身上。

 class Widget{
    public:
        static void* operator new(std::size_t size, std::ostream& logStream)//非正常形式的new
            throw(std::bad_alloc);
        static void operator delete(void* pMemory, std::size_t size)//正常的class专属delete
            throw();
        ……
    };
Widget* pw = new(std::cerr) Widget;

我们看到上面的代码,当调用new(std::cerr) Widget语句时,我们就进入了Widget的自定义new函数中,当构造函数中出现异常的时候,我们可以在构造函数中调用对应的delete函数,实现内存回收。大家如果得到签名式,回收内存就简单了。

我们先来介绍一下placement new 和 placement delete的介绍,术语placement new意味着带有额外参数的new,如void* operator new(std::size_t, void* pMemory) throw();,placement delete味着带有额外参数的delete。

解决问题的关键变成了如何得到合适的 delete。

根据作者的说明,我们知道

placement delete只有在“伴随placement new调用而触发的构造函数”出现异常时才会被调用。对一个指针施行delete绝不会导致调用placement delete。

这意味对所有placement new我们必须同时提供一个正常的delete和一个placement版本。

class Derived: public Base{
    public:
        ……
        static void* operator new(std::size_t size) throw(std::bad_alloc);//重新声明正常形式的new
    };
    Derived* pd=new (std::clog) Derived;//错误,因为Base的placement new被掩盖了
    Derived* pd1=new Derived;//正确
在缺省情况下,C++在global作用域内提供以下形式的operator new:

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

class StadardNewDeleteForms{
    public:
        //normal
        static void* operator new(std::size_t size) throw(std::bad_alloc)
        {return ::operator new(size);}
        static void operator delete(void* pMemory) throw()
        {::operator delete(pMemory);}
        //placement
        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()
        {::operator delete(pMemory, ptr);}
        //nothrow
        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&) throw()
        {::operator delete(pMemory);}
    };

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 detele(std::size_t size, std::ostream& logStream) throw();
    };
时间: 2024-10-07 04:49:30

Effective C++ 条款52的相关文章

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(v

Effective C++ 条款 52:写了placement new也要写placement delete

(一) 当我们写下了下面这个语句: Widget* pw = new Widget; 这个时候,共有两个函数被调用:一个分配内存的operator new,另外一个是Widget的default构造函数. 假设第一个调用成功,第二个却抛出异常.步骤一所分配内存必须取消并恢复旧观,否则会造成内存泄漏.这时,客户没能力归还内存,因为Widget构造函数抛出异常,pw尚未被赋值,客户手上也就没有指针指向该被归还的内存. 这个时候,取消步骤一,并恢复旧观的责任就落到C++运行系统身上. 运行期系统会高兴

More Effective C++ 条款35 让自己习惯于标准C++ 语言

(由于本书出版于1996年,因此当时的新特性现在来说可能已经习以为常,但现在重新了解反而会起到了解C++变迁的作用) 1. 1990年后C++的重要改变 1). 增加了新的语言特性:RTTI,namespaces,bool,关键词mutable和explicit,enums作为重载函数之自变量所引发的类型晋升转换,以及"在class 定义区内直接为整数型(intergral) const static class members设定初值"的能力. 2). 扩充了Templates的特性

effective c++ 条款4 make sure that objects are initialized before they are used

1 c++ 类的数据成员的初始化发生在构造函数前 class InitialData { public: int data1; int data2; InitialData(int a, int b) { data1 = a: //this is assignment data2 = b; //this is assignment } /* InitialData(int a, int b):data1(a),data2(b) //this is initial {} */ } 2 不同cpp文

More Effective C++ 条款34 如何在一个程序中结合C++和C

1. C++和C混合使用的前提之一就是编译器产生兼容的目标文件(.lib和.dll等).所谓"兼容",指的是编译器在"预编译器相依的特性上"一致,如int和double大小,参数压栈机制等,只有在这个基础上才能讨论结合使用C++和C模块的问题. 2. 在1的基础上,要结合使用C++和C的模块,主要有以下几点需要注意: 1). name mangling(名称重整) Name mangling是C++用于支持函数重载的机制,它对重载的函数名称进行一定改变,使得每个函数

Effective C++ 条款3 尽可能用const

1. const可被施加于任何作用域内的对象,函数参数,函数返回类型,成员函数本体.用const修饰指针,如果const出现在*之前,表明指针不能更改所指向的对象的内容,如果const出现在*之后,表明指针只能指向同一块内存.另外int const*p和const int*p含义相同.如果对象成员有普通指针,那么构造该类的一个const对象时,const修饰使得该指针只能指向同一块内存,但指针指向的内容可以改变. 2. 将某些东西声明为const可以帮助编译器侦测出错误用法. 3. 编译器强制实

effective c++ 条款13 use object to manage resources.

请求的系统资源需要最终还回系统,为了避免遗忘返还这个动作,可以利用析构函数在object销毁时自动调用的特点来实现. 简单说就是用object来管理资源. 以内存资源为例 class Investment {}; Investment* creatInvestment(){...} // factory function to produce investment object void main() { Investment* pInv = creatInvestment();//call t

effective c++ 条款3 use const whereever you can

1 const 传达的意思应该是这个变量是常量不能更改 2 const 在 * 左边表示数据是const,在右边表示指针是const // char greeting[] = "hello"; char* p = greeting; //const *: const data //* const: const pointer char const *p1 = greeting; // const data char * const p2 = greeting; // const poi

effective c++ 条款18 make interface easy to use correctly and hard to use incorrectly

举一个容易犯错的例子 class Date { private: int month; int day; int year; public: Date(int month,int day,int year) { this->month = month; ... } } //wrong example Date date(30,3,1995);//should be 3,30 Date date(2,30,1995);//should be 3,30 使用类型可避免这个问题 class Month