placement new 讲解

news/2024/7/4 0:51:06 标签: delete, string, class, buffer, object, c
cle class="tags" href="/tags/CLASS.html" title=class>class="baidu_pl">
cle_content" class="tags" href="/tags/CLASS.html" title=class>class="article_content clearfix">
content_views" class="tags" href="/tags/CLASS.html" title=class>class="htmledit_views">

color:rgb(85,85,85)"> placement new 是重载operator new的一个标准、全局的版本࿰c;它不能被自定义的版本代替(不像普通的operator new和operator class="tags" href="/tags/CLASS.html" title=class>class="tags" href="/tags/DELETE.html" title=delete>delete能够被替换成用户自定义的版本)。color:rgb(255,0,0); font-family:Helvetica,Arial,sans-serif; text-align:left; font-size:18px">placement new的作用就是:创建对象(调用该类的构造函数)但是不分配内存࿰c;而是在已有的内存块上面创建对象。用于需要反复创建并删除的对象上࿰c;可以降低分配释放内存的性能消耗。

color:rgb(85,85,85)">

color:rgb(85,85,85)"> 它的原型如下: 
void *operator new( size_t, void *p ) throw()  { return p; }

color:rgb(85,85,85)">

color:rgb(85,85,85)">

color:rgb(85,85,85)"> 首先我们区分下几个容易混淆的关键词:new、operator new、placement new 
new和class="tags" href="/tags/CLASS.html" title=class>class="tags" href="/tags/DELETE.html" title=delete>delete操作符我们应该都用过࿰c;它们是对堆中的内存进行申请和释放࿰c;而这两个都是不能被重载的。要实现不同的内存分配行为࿰c;需要重载operator new࿰c;而不是new和class="tags" href="/tags/CLASS.html" title=class>class="tags" href="/tags/DELETE.html" title=delete>delete。

color:rgb(85,85,85)"> 看如下代码: 
class="tags" href="/tags/CLASS.html" title=class>class MyClass {…}; 
MyClass * p=new MyClass;

color:rgb(85,85,85)"> 这里的new实际上是执行如下3个过程:

color:rgb(85,85,85)"> 1. 调用operator new分配内存 ;2. 调用构造函数生成类对象;3. 返回相应指针。

color:rgb(85,85,85)"> operator new就像operator+一样࿰c;是可以重载的࿰c;但是不能在全局对原型为void operator new(size_t size)这个原型进行重载࿰c;一般只能在类中进行重载。如果类中没有重载operator new࿰c;那么调用的就是全局的::operator new来完成堆的分配。同理࿰c;operator new[]、operator class="tags" href="/tags/CLASS.html" title=class>class="tags" href="/tags/DELETE.html" title=delete>delete、operator class="tags" href="/tags/CLASS.html" title=class>class="tags" href="/tags/DELETE.html" title=delete>delete[]也是可以重载的࿰c;一般你重载的其中一个࿰c;那么最后把其余的三个都重载一遍。

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

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

color:rgb(85,85,85)">     使用方法如下: 
1. 缓冲区提前分配 
可以使用堆的空间࿰c;也可以使用栈的空间࿰c;所以分配方式有如下两种: 
class="tags" href="/tags/CLASS.html" title=class>class MyClass {…}; 
char *buf=new char[N*sizeof(MyClass)+sizeof(int)];或者char buf[N*sizeof(MyClass)+sizeof(int)];

color:rgb(85,85,85)"> 2. 对象的构造 
MyClass * pClass=new(buf) MyClass;

color:rgb(85,85,85)"> 3. 对象的销毁 
一旦这个对象使用完毕࿰c;color:rgb(255,0,0)">你必须显式的调用类的析构函数进行销毁对象。但此时内存空间不会被释放࿰c;以便其他的对象的构造。 
pClass->~MyClass();

color:rgb(85,85,85)"> 4. 内存的释放 
如果缓冲区在堆中࿰c;那么调用class="tags" href="/tags/CLASS.html" title=class>class="tags" href="/tags/DELETE.html" title=delete>delete[] buf;进行内存的释放;如果在栈中࿰c;那么在其作用域内有效࿰c;跳出作用域࿰c;内存自动释放。

color:rgb(85,85,85)">

color:rgb(85,85,85)">

color:rgb(85,85,85)"> 注意:

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

color:rgb(85,85,85)"> [使用方法]

color:rgb(85,85,85)"> 在很多情况下࿰c;placement new的使用方法和其他普通的new有所不同。这里提供了它的使用步骤。

color:rgb(85,85,85); font-family:Consolas; font-size:14px; line-height:18px">第一步  缓存提前分配

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

color:rgb(85,85,85)"> color:rgb(0,102,255)">class="tags" href="/tags/CLASS.html" title=class>class Task ;

color:rgb(85,85,85)"> color:rgb(0,102,255)">char * buff = new [sizeof(Task)]; color:rgb(0,102,0)">//分配内存

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

color:rgb(85,85,85); font-family:Consolas; font-size:14px; line-height:18px">第二步:对象的分配

color:rgb(85,85,85)"> 在刚才已分配的缓存区调用placement new来构造一个对象。

color:rgb(85,85,85)"> color:rgb(0,102,255)">Task *ptask = new(buff) Task

color:rgb(85,85,85); font-family:Consolas; font-size:14px; line-height:18px">第三步:使用

color:rgb(85,85,85)"> 按照普通方式使用分配的对象:

color:rgb(85,85,85)"> color:rgb(0,102,255)">ptask->suspend();

color:rgb(85,85,85)"> color:rgb(0,102,255)">ptask->resume();

color:rgb(85,85,85)"> color:rgb(0,102,0)">//...

color:rgb(85,85,85)">

color:rgb(85,85,85); font-family:Consolas; font-size:14px; line-height:18px">第四步:对象的毁灭

color:rgb(85,85,85)"> 一旦你使用完这个对象࿰c;你必须调用它的析构函数来毁灭它。按照下面的方式调用析构函数:

color:rgb(85,85,85)"> color:rgb(0,102,255)">ptask->~Task(); color:rgb(0,102,0)">//调用外在的析构函数

color:rgb(85,85,85)">

color:rgb(85,85,85); font-family:Consolas; font-size:14px; line-height:18px">第五步:释放

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

color:rgb(85,85,85)"> color:rgb(0,102,255)">class="tags" href="/tags/CLASS.html" title=class>class="tags" href="/tags/DELETE.html" title=delete>delete [] buff;

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

color:rgb(85,85,85)">

color:rgb(85,85,85)"> 【Q & A】

color:rgb(85,85,85)"> 1、placement new 为何物?

color:rgb(85,85,85)"> placement new 是重载operator new 的一个标准、全局的版本࿰c;它不能够被自定义的版本代替(不像普通版本的operator new 和 operator class="tags" href="/tags/CLASS.html" title=class>class="tags" href="/tags/DELETE.html" title=delete>delete能够被替换)。

color:rgb(85,85,85)"> void *operator new( size_t, void *p ) throw()

color:rgb(85,85,85)">     { return p; }

color:rgb(85,85,85)"> placement new的执行忽略了size_t参数࿰c;只返还第二个参数。其结果是允许用户把一个对象放到一个特定的地方࿰c;达到调用构造函数的效果。

color:rgb(85,85,85)"> class="tags" href="/tags/CLASS.html" title=class>class SPort { ... }; // represents a serial port

color:rgb(85,85,85)"> const int comLoc = 0x00400000; // location of a port

color:rgb(85,85,85)"> //...

color:rgb(85,85,85)"> void *comAddr = reinterpret_cast<void *>(comLoc);

color:rgb(85,85,85)"> SPort *com1 = new (comAddr) SPort; // create class="tags" href="/tags/OBJECT.html" title=object>object at comLoc

color:rgb(85,85,85)"> com1->~SPort(); //释放

color:rgb(85,85,85)">  

color:rgb(85,85,85)"> 2、new 、operator new 和 placement new 一样吗?

color:rgb(85,85,85)"> new :不能被重载࿰c;其行为总是一致的。它先调用operator new分配内存࿰c;然后调用构造函数初始化那段内存。

color:rgb(85,85,85)"> operator new:要实现不同的内存分配行为࿰c;应该重载operator new࿰c;而不是new。

color:rgb(85,85,85)"> class="tags" href="/tags/CLASS.html" title=class>class="tags" href="/tags/DELETE.html" title=delete>delete和operator class="tags" href="/tags/CLASS.html" title=class>class="tags" href="/tags/DELETE.html" title=delete>delete类似。

color:rgb(85,85,85)"> placement new:只是operator new重载的一个版本。它并不分配内存࿰c;只是返回指向已经分配好的某段内存的一个指针。因此不能删除它࿰c;但需要调用对象的析构函数。

color:rgb(85,85,85)">  

color:rgb(85,85,85)"> 3、在已有的内存上用placement new分配数组

color:rgb(85,85,85)"> const int numComs = 4;

color:rgb(85,85,85)"> //...

color:rgb(85,85,85)"> SPort *comPorts = new (comAddr) SPort[numComs]; // create array

color:rgb(85,85,85)"> int i = numComs;

color:rgb(85,85,85)"> while( i )

color:rgb(85,85,85)">     comPorts[--i].~SPort();

color:rgb(85,85,85)">  

color:rgb(85,85,85)"> 4、用Placement new 解决class="tags" href="/tags/BUFFER.html" title=buffer>buffer的问题

color:rgb(85,85,85)"> 用new分配的数组缓冲时࿰c;由于调用了默认构造函数࿰c;因此执行效率上不佳。若没有默认构造函数则会发生编译时错误。用Placement new可以解决此类问题。

color:rgb(85,85,85)"> const size_t n = sizeof(class="tags" href="/tags/CLASS.html" title=class>class="tags" href="/tags/STRING.html" title=string>string) * BUFSIZE;

color:rgb(85,85,85)"> class="tags" href="/tags/CLASS.html" title=class>class="tags" href="/tags/STRING.html" title=string>string *sbuf = static_cast<class="tags" href="/tags/CLASS.html" title=class>class="tags" href="/tags/STRING.html" title=string>string *>(::operator new( n ));

color:rgb(85,85,85)"> int size = 0;

color:rgb(85,85,85)"> //此时࿰c;class="tags" href="/tags/BUFFER.html" title=buffer>buffer还没有初始化࿰c;因此需要用 placement new 调用copy构造函数初始化。

color:rgb(85,85,85)"> void append( class="tags" href="/tags/CLASS.html" title=class>class="tags" href="/tags/STRING.html" title=string>string buf[], int &size, const class="tags" href="/tags/CLASS.html" title=class>class="tags" href="/tags/STRING.html" title=string>string &val )

color:rgb(85,85,85)">     { new (&buf[size++]) class="tags" href="/tags/CLASS.html" title=class>class="tags" href="/tags/STRING.html" title=string>string( val ); } // placement new

color:rgb(85,85,85)"> //最后的清理

color:rgb(85,85,85)"> void cleanupBuf( class="tags" href="/tags/CLASS.html" title=class>class="tags" href="/tags/STRING.html" title=string>string buf[], int size ) {

color:rgb(85,85,85)">     while( size )

color:rgb(85,85,85)">         buf[--size].~class="tags" href="/tags/CLASS.html" title=class>class="tags" href="/tags/STRING.html" title=string>string(); // destroy initialized elements

color:rgb(85,85,85)">     ::operator class="tags" href="/tags/CLASS.html" title=class>class="tags" href="/tags/DELETE.html" title=delete>delete( buf ); // free storage

color:rgb(85,85,85)"> }

cle>

http://www.niftyadmin.cn/n/790736.html

相关文章

C++ explicit关键字应用于构造函数

C explicit关键字主要是用来对构造函数进行修饰&#xff0c;来表明这一构造函数是显示的。那么在这里大家就一起来看看其正确的使用方法吧。 C编程语言可以被看做是C语言的一个升级版本&#xff0c;其中有很多应用方式与C语言相似&#xff0c;但同时又比C语言功能更加强大&…

C++里的静态成员函数不能用const的原因

static在c中的第五种含义&#xff1a;用static修饰不访问非静态数据成员的类成员函数。这意味着一个静态成员函数只能访问它的参数、类的静态数据成员和全局变量。不能用const的原因&#xff1a; 这是C的规则&#xff0c;const修饰符用于表示函数不能修改成员变量的值&#xff…

为什么内联函数,构造函数,静态成员函数不能为virtual函数

为什么内联函数&#xff0c;构造函数&#xff0c;静态成员函数不能为virtual函数&#xff1f; 1> 内联函数 内联函数是在编译时期展开,而虚函数的特性是运行时才动态联编,所以两者矛盾,不能定义内联函数为虚函数。 2> 构造函数 构造函数用来创建一个新的对象,而虚函数的运…

const 和 volatile 成员函数、mutable 数据成员

const 和 volatile 成员函数、mutable 数据成员 为尊重类对象的常量性&#xff0c;编译器必须区分不安全与安全的成员函数(即区分试图修改类对象与不试图修改类对象的函数)。 const成员函数&#xff1a; 类的设计者通过把成员函数声明为 const &#xff0c;以表明它们不修…

对集成学习的初步理解

对集成学习的初步理解 最近在看一些集成学习方面的知识&#xff0c;其中南京大学的周志华教授写的几篇关于集成学习综述性的文章还不错。看了下对集成学习有了一个初步的了解&#xff0c;如下&#xff1a; 集成学习是机器学习中一个非常重要且热门的分支&#xff0c;是用多个弱…

bootstrap bagging boosting

bootstrap bagging boosting这几个概念经常用到&#xff0c;现仔细学习了一下&#xff1a;他们都属于集成学习方法&#xff0c;(如:Bagging&#xff0c;Boosting&#xff0c;Stacking)&#xff0c;将训练的学习器集成在一起,原理来源于PAC学习模型&#xff08;Probably Approxi…

回归(regression)、梯度下降(gradient descent)

版权声明&#xff1a; 本文由LeftNotEasy所有&#xff0c;发布于http://leftnoteasy.cnblogs.com。如果转载&#xff0c;请注明出处&#xff0c;在未经作者同意下将本文用于商业用途&#xff0c;将追究其法律责任。 前言: 上次写过一篇关于贝叶斯概率论的数学&#xff0c;最近时…

线性回归,偏差、方差权衡

版权声明&#xff1a; 本文由LeftNotEasy所有&#xff0c;发布于http://leftnoteasy.cnblogs.com。如果转载&#xff0c;请注明出处&#xff0c;在未经作者同意下将本文用于商业用途&#xff0c;将追究其法律责任。如果有问题&#xff0c;请联系作者 wheeleastgmail.com 前言&a…