• Placement New


    第一部分:基本概念

    1. new和operator new

    看如下代码:
    class MyClass {…};
    MyClass * p=new MyClass;

    这里的new实际上是执行如下3个过程:

    1. 调用operator new分配内存 ;2. 调用构造函数生成类对象;3. 返回相应指针。

    operator new就像operator+一样,是可以重载的,但是不能在全局对原型为void operator new(size_t size)这个原型进行重载,一般只能在类中进行重载。如果类中没有重载operator new,那么调用的就是全局的::operator new来完成堆的分配。同理,operator new[]、operator delete、operator delete[]也是可以重载的,一般你重载的其中一个,那么最后把其余的三个都重载一遍。

    你一般不会直接调用operator new,但是一旦这么做,你可以象调用其它函数一样调用它:

    void *rawMemory = operator new(sizeof(string));

    操作符operator new将返回一个指针,指向一块足够容纳一个string类型对象的内存。

    2. Placement new

    有时你确实想直接调用构造函数。在一个已存在的对象上调用构造函数是没有意义的,因为构造函数用来初始化对象,而一个对象仅仅能在给它初值时被初始化一 次。但是有时你有一些已经被分配但是尚未处理的的(raw)内存,你需要在这些内存中构造一个对象。你可以使用一个特殊的operator new ,它被称为placement new。

    其实它也只是operator new的一个重载的版本,只是我们很少用到它。如果你想在已经分配的内存中创建一个对象,使用new时行不通的。也就是说placement new允许你在一个已经分配好的内存中(栈或者堆中)构造一个新的对象。原型中void*p实际上就是指向一个已经分配好的内存缓冲区的的首地址。

    我们知道使用new操作符分配内存需要在堆中查找足够大的剩余空间,这个操作速度是很慢的,而且有可能出现无法分配内存的异常(空间不够)。 placement new就可以解决这个问题。我们构造对象都是在一个预先准备好了的内存缓冲区中进行,不需要查找内存,内存分配的时间是常数;而且不会出现在程序运行中途 出现内存不足的异常。所以,placement new非常适合那些对时间要求比较高,长时间运行不希望被打断的应用程序。

    placement new是标准C++库的一部分。为了使用placement new,你必须使用语句#include <new>。

    3. 非常棒的比较总结

    你想在堆上建立一个对象,应该用new操作符。它既分配内存又为对象调用构造函数。如果你仅仅想分配内存,就应该调用operator new函数;它不会调用构造函数。如果你想定制自己的在堆对象被建立时的内存分配过程,你应该写你自己的operator new函数,然后使用new操作符,new操作符会调用你定制的operator new。如果你想在一块已经获得指针的内存里建立一个对象,应该用placement new。

    第二部分:Placement new的用法(收藏)

    STL里的<new>由一个特别的操作符new来定义,这个就是我们熟知的placement new。和其他普通的new不同的是,它在括号里多了另外一个参数。比如:

    Widget * p = new Widget; //ordinary new  //普通的new pi = new (ptr) int;
    pi = new (ptr) int; //placement new

    括号里的参数是一个指针,它指向一个内存缓冲器,placement new将在这个缓冲器上分配一个对象。Placement new的返回值是这个被构造对象的地址(比如括号中的传递参数)。placement new主要适用于:在对时间要求非常高的应用程序中,因为这些程序分配的时间是确定的;长时间运行而不被打断的程序;以及执行一个垃圾收集器(garbage collector)。

    使用方法

    在很多情况下,placement new的使用方法和其他普通的new有所不同。这里提供了它的使用步骤。

    第一步  缓存提前分配

    为了保证通过placement new使用的缓存区的memory alignmen(内存队列)正确准备,使用普通的new来分配它:

    class Task ;

    char * buff = new [sizeof(Task)]; //分配内存

    (请注意auto或者static内存并非都正确地为每一个对象类型排列,所以,你将不能以placement new使用它们。)

    第二步:对象的分配

    在刚才已分配的缓存区调用placement new来构造一个对象。

    Task *ptask = new(buff) Task

    第三步:使用

    按照普通方式使用分配的对象:

    ptask->suspend();

    ptask->resume();

    //...

    第四步:对象的毁灭

    一旦这个对象使用完毕,你必须显式的调用类的析构函数进行销毁对象。按照下面的方式调用析构函数:

    ptask->~Task(); //调用外在的析构函数

    但此时内存空间不会被释放,以便其他的对象的构造。

    第五步:释放

    你可以反复利用缓存并给它分配一个新的对象(重复步骤2,3,4)如果你不打算再次使用这个缓存,你可以象这样释放它:

    delete [] buff;

    跳过任何步骤就可能导致运行时间的崩溃,内存泄露,以及其它的意想不到的情况。如果你确实需要使用placement new,请认真遵循以上的步骤。


    注意:

    • 在C++标准中,对于placement operator new []有如下的说明: placement operator new[] needs implementation-defined amount of additional storage to save a size of array. 所以我们必须申请比原始对象大小多出sizeof(int)个字节来存放对象的个数,或者说数组的大小。
    • 使用方法第二步中的new才是placement new,其实是没有申请内存的,只是调用了构造函数,返回一个指向已经分配好的内存的一个指针,所以对象销毁的时候不需要调用delete释放空间,但必须调用析构函数销毁对象。


  • 相关阅读:
    springboot 定制错误页面
    Maven私有仓库-使用docker部署Nexus
    centos7 sentry部署指南
    静态文件服务器部署指南
    开始使用ansible
    2016项目开发经验总结及后续计划
    WPF 3D模型 3D场景
    压缩日志的方法
    创建动态视图
    如何 ︰ 执行批量更新和插入使用.NET 提供程序在 C#.NET OpenXML
  • 原文地址:https://www.cnblogs.com/taoxu0903/p/1396861.html
Copyright © 2020-2023  润新知