通常,new负责在堆(heap)中找到一个能够满足要求的内存块,new操作符还有另外一种变体,称为布局(placement)new操作符,它能够让你指定要使用的位置。
要使用布局new,首先要包含头文件new,它后面需要一个提供地址的参数。话不多说看代码。
// newplace.cpp -- using placement new
#include <iostream>
#include <new> // for placement new
const int BUF = 512;
const int N = 5;
char buffer[BUF]; // chunk of memory
int main( )
{
using namespace std;
double *pd1 , *pd2;
int i;
cout << "Calling new and placement new:\n";
pd1 = new double[N]; // use heap
pd2 = new ( buffer ) double[N]; // use buffer array
for( i = 0; i < N; i++ )
pd2[i] = pd1[i] = 1000 + 20.0 * i;
cout << "Memory addresses:\n" << " heap: " << pd1 //常规new在堆上分配内存的首地址
<< " static: " << ( void * ) buffer << endl; //布局new在buffer上分配的首地址
cout << "Memory contents:\n";
for( i = 0; i < N; i++ )
{
cout << pd1[i] << " at " << &pd1[i] << "; ";
cout << pd2[i] << " at " << &pd2[i] << endl;
}
cout << "\nCalling new and placement new a second time:\n";
double *pd3 , *pd4;
pd3 = new double[N]; // find new address
pd4 = new ( buffer ) double[N]; // overwrite old data
for( i = 0; i < N; i++ )
pd4[i] = pd3[i] = 1000 + 40.0 * i;
cout << "Memory contents:\n";
for( i = 0; i < N; i++ )
{
cout << pd3[i] << " at " << &pd3[i] << "; ";
cout << pd4[i] << " at " << &pd4[i] << endl;
}
cout << "\nCalling new and placement new a third time:\n";
delete[] pd1;
pd1 = new double[N];
pd2 = new ( buffer + N * sizeof( double ) ) double[N];
for( i = 0; i < N; i++ )
pd2[i] = pd1[i] = 1000 + 60.0 * i;
cout << "Memory contents:\n";
for( i = 0; i < N; i++ )
{
cout << pd1[i] << " at " << &pd1[i] << "; ";
cout << pd2[i] << " at " << &pd2[i] << endl;
}
delete[] pd1;
delete[] pd3;
// cin.get();
return 0;
}
下面是程序的输出(不同电脑运行结果可能不同)
我们可以看到
1:布局new确实将p2放在了buffer中,p1则是放在堆中
2:在第二次操作时,常规new查找的是一块新的内存块,布局new分配和以前相同的内存,说明布局new只传递地址,并不跟踪该内存块是否使用过,也不查找未使用的内存块,内存管理的负担交给程序猿
3:是否使用delete来释放内存——对于常规new,delete [ ] pd1;释放最初申请的内存,接下来再次调用new时该内存块可用,程序并没有使用delete来释放布局new分配的内存,这个例子中不能这么做。buffer是静态内存,delete只能用于常规new分配的堆内存,数组buffer位于delete的管辖区之外。当然,如果buffer是用常规new来创建的,也可以用delete释放。
将布局new用于类对象时,情况更复杂。话不多说,先上代码。
#include <iostream>
#include <string>
#include <new>
using namespace std;
const int BUF = 512;
class JustTesting
{
private:
string words;
int number;
public:
JustTesting( const string & s = "Just Testing" , int n = 0 )
{
words = s; number = n; cout << words << " constructed\n";
}
~JustTesting( ) { cout << words << " destroyed\n"; }
void Show( ) const { cout << words << ", " << number << endl; }
};
int main( )
{
char * buffer = new char[BUF]; // get a block of memory
JustTesting *pc1 , *pc2;
pc1 = new ( buffer ) JustTesting; // place object in buffer
pc2 = new JustTesting( "Heap1" , 20 ); // place object on heap
cout << "Memory block addresses:\n" << "buffer: "
<< ( void * ) buffer << " heap: " << pc2 << endl;
cout << "Memory contents:\n";
cout << pc1 << ": ";
pc1->Show( );
cout << pc2 << ": ";
pc2->Show( );
JustTesting *pc3 , *pc4;
// fix placement new location
pc3 = new ( buffer + sizeof( JustTesting ) ) //非优化版pc3 = new (buffer) JustTesting("Bad Idea", 6);
JustTesting( "Better Idea" , 6 );
pc4 = new JustTesting( "Heap2" , 10 );
cout << "Memory contents:\n";
cout << pc3 << ": ";
pc3->Show( );
cout << pc4 << ": ";
pc4->Show( );
delete pc2; // free Heap1
delete pc4; // free Heap2
// explicitly destroy placement new objects
pc3->~JustTesting( ); // destroy object pointed to by pc3 非优化版无此行
pc1->~JustTesting( ); // destroy object pointed to by pc1 非优化版无此行
delete[] buffer; // free buffer
// std::cin.get();
return 0;
}
非优化版本的运行结果如图所示:
优化版本运行结果如下入所示:
对运行结果的差异做出几点说明:
1:程序员想用布局new创建多个对象时,必须提供两个不同的缓冲区地址,否则新对象会覆盖第一个对象的内存空间
2:使用布局new为对象分配内存时,必须确保析构函数被调用,但同时,我们又不能使用 delete pc1;这样的方式来做,因此必须显式地为使用布局new操作符创建的对象调用析构函数,我们删除的时候,应当以创建顺序相反的顺序进行删除,因此晚创建的对象可能依赖于早创建的对象。仅当所有对象都被销毁后,才能释放存储这些对象的缓冲区。
推荐一篇博文,讲得很好。
http://www.cnblogs.com/sunrunner/p/3716134.html