zoukankan      html  css  js  c++  java
  • new表达式,operator new和placement new介绍

    new/delete是c++中动态构造对象的表达式 ,一般情况下的new/delete都是指的new/delete表达式,这是一个操作符,和sizeof一样,不能改变其意义。

    new/delete表达式的声明如下:

    ::(optional) new (placement_params)(optional) ( type )initializer(optional)
    ::(optional) delete  expression  

    除了全局作用符::和初始化参数,还有个 placement_params,这是不常见的,要理解这个参数的作用,就要了解operator new和placement new。

    众所周知,new表达式做了两个工作:1.分配内存;2.在分配的内存上调用构造函数构造对象。比如我们分配一个string对象

    string *str = new string(“Kian”);

    编译器首先调用operator new分配一块内存,类似于malloc,然后在mem上面调用构造函数,

    1.void *mem = operator new(sizeof(string));

    2. create string at men.

    第二步我们是控制不了的,但是operator new却是可以修改的。

    Operator new/delete的声明如下:

    void* operator new  ( std::size_t count );
    void* operator new  ( std::size_t count, const std::nothrow_t& tag);
    void operator delete  ( void* ptr );
    void operator delete  ( void* ptr, const std::nothrow_t& tag);

    第二种带参数tag的声明称为nothrow形式,因为现在的operator new如果分配内存失败的话会抛出bad_alloc异常. 有时候我们不想抛出异常,而是根据返回值判断内存分配失败与否,nothrow形式就是这个作用,失败时不抛出异常,而是返回null指针。

    我们可以直接重载operator new, 定制自己的内存分配策略,常见的作用是优化内存使用性能。重载operator new不需要看见声明就可以直接使用。我们重定义一个简单的版本:

    void* operator new(std::size_t size){
      printf("operator new called, size=%d
    ", size);
      return malloc(size);
    }
     
    void operator delete(void *ptr){
      printf("operator delete called
    ");
      free(ptr);
    }
    
    int main(){
      int *i = new int(2);
      delete i;
      std::string *str = new std::string("Kian");
      delete str;
    }

    运行结果:

    operator new called, size=4

    operator delete called

    operator new called, size=4

    operator new called, size=17

    operator delete called

    operator delete called

    可是有时候我们希望拥有更多的功能,比如记录内存分配释放的位置,用于检测内存错误,或者直接在已有的内存上构造对象,那么必须定义更多参数,这就需要placement new/delete,声明如下.

    void* operator new  ( std::size_t count, void* ptr );
    void* operator new  ( std::size_t count, user-defined-args... );
    void operator delete  ( void* ptr, void* place);
    void operator delete  ( void* ptr, user-defined-args...); 

    可以直接在已有内存上构造对象:

    void *mem = (void*)malloc(sizeof(int));
    int *j = new(mem) int(3);
    printf("mem=%p, j=%p, *j = %d
    ",mem, j, *j);
    free(mem);

    运行结果:

    mem=0x8a48008, j=0x8a48008, *j = 3

    可以看出new直接在mem上面构造了对象。

    目前,void* operator new  ( std::size_t count, void* ptr )在全局域还不能被重载,但是void* operator new  ( std::size_t count, user-defined-args... )可以自由定义。

    比如记录内存分配发生的位置:

    void* operator new(std::size_t size){
      printf("operator new called, size=%d
    ", size);
      return malloc(size);
    }
    
    void operator delete(void *ptr){
      printf("operator delete called
    ");
      free(ptr);
    }
    
    void* operator new(std::size_t size, char* filename, int line){
      printf("new called at %s:%d  size=%d 
    ", filename, line, size);
      return ::operator new(size);
    }
    
    void operator delete(void *place, char* filename, int line) {
      printf("delete called at %s:%d  place=%p 
    ", filename, line, place);
      ::operator delete(place);
    }
    
    int main(){
      int *k = new(__FILE__, __LINE__) int(1);
      printf("k=%p *k=%d 
    ", k, *k);
      delete k;
    }

    运行结果:

    new called at testnew.cpp:41  size=4

    operator new called, size=4

    k=0x847f008 *k=1

    operator delete called

    在每个new中打印了文件名和行号,不过细心的你会发现delete时并没有调用重载的placement delete ,这个delete只有在构造对象时抛出了异常才会调用,我们写一个简单的class来看看:

    class ThrowExcept {
     public:
      ThrowExcept(int v):value_(v){ throw 1;}
     private:
      int value_;
    };
    
    int main(){
      ThrowExcept *t;
      try{
      t = new(__FILE__, __LINE__) ThrowExcept(10);
      }catch(int &e){
        printf("catch exception %d
    ", e);
      }
    }

    运行结果:

    new called at testnew.cpp:53  size=4

    operator new called, size=4

    delete called at testnew.cpp:53  place=0x9e2e008

    operator delete called

    catch exception 1

    自定义的delete被正常调用,这么做的原因在于如果构造函数抛出异常,系统正常的operator delete并不知道用户自定义的placement new做了什么,自然也不知道怎么去释放。所以如果自己定义placement new, 一定要定义对应的palcement delete,不然可能出现memory leak。

     

    http://en.cppreference.com/w/cpp/memory/new/operator_new

    http://en.cppreference.com/w/cpp/language/new

    《effective/more effective c++》

  • 相关阅读:
    使用Maven快速创建一个SpringMVC工程步骤
    签到
    yaml简介
    APP定位元素之UiSelector
    js中var、let、const区别
    用Jquery去写树结构
    正则相关的知识分享
    python常见问题
    Vue.js 的一些小技巧
    关于jsp删除成功,添加成功等之后 页面自动跳转的js写法
  • 原文地址:https://www.cnblogs.com/coderkian/p/3688822.html
Copyright © 2011-2022 走看看