/*
*C++ 语言学习
* —— placement 语法
* 内容来自网上及摘自 C++ Strategies and Tactics
*/
Placement New
关键字new可以接受参数:
p = new(arg1, arg2, arg3) D;
这些参数会被隐含地传给new操作函数:
p = operator new(sizeof(D), arg1, arg2, arg3);
注意,第一个参数仍然是要生成对象的字节数,其它参数总是跟在它后面。
标准运行库定义了一个new操作的特别重载版本,它接受一个额外参数:
void * operator new(std::size_t, void *);
这种形式的new操作被如下的语句隐含调用:
p = new(addr) D;
这里,addr是某些数据区的地址,并且类型兼容于void *。addr传给这个特别的new操作,这个特别的new操作和其它new操作一样返回将被构造的内存的地址,但不需要在自由内存区中再申请内存,它直接将addr返回:
void *operator new(std::size_t, void *addr)
{
return addr;
}
这个返回值然后被传给D::D作构造函数的this指针。
就这样,表达式:
p = new(addr) D;
在addr所指的内存上构造了一个D对象,并将p赋为addr的值。这个方法让你有效地指定新生成对象的位置,所以被叫作“placement new”。
这个new的额外参数形式最初被设计为控制对象的位置的,但是C++标准委员会认识到这样的传参体系可以被用于任意用途而不仅是控制对象的位置。不幸的是,术语“placement”已经被根据最初目的而制订,并适用于所有new操作的额外参数的形式,即使它们根本不试图控制对象的位置。
所以,下面每个表达式都是placement new的一个例子:
new(addr) D; // calls operator new(std::size_t, void *)
new(addr, 3) D; // calls operator new(std::size_t, void *, int)
new(3) D; // calls operator new(std::size_t, int)
即使只有第一个形式是一般被用作控制对象位置的。
placement Delete
现在,只要认为 placement delete 是有用处的就行了。我肯定会讲述理由的,可能就在接下来的两篇内。
Placement new操作和placement delete操作必须成对出现。
一般来说,每一个
void *operator new(std::size_t, p1, p2, p3, ..., pN);
都对应一个
void operator delete(void *, p1, p2, p3, ..., pN);
根据这条原则,标准运行库定义了
void operator delete(void *, void *);
以对应我刚讲的placement new操作。
数组New和数组Delete
基于对称,标准运行库也申明了placement new[]操作和placement delete[]操作:
void *operator new[](std::size_t, void *);
void operator delete[](void *, void *);
如你所料:placement new[]操作返回传入的地址,而placement delete[]操作的行为和我没有细述的placement delete操作行为几乎一样。
下面摘自《C++ Strategies and Tactics》
通过在紧接着 new 关键字后使用括号列表,用户可以向 operator new 传递参数:Memory_info info;
Thing * tp = new (info) Thing;
这些参数被作为额外的参数传递给 operator new ,他们被放置在隐藏的第一个(类型为 size_t, 用来指示要分配的字节数)参数后,我们必须提供一个 operator new (无论是和类相关的还是全局的)来使用这些参数。因此,在上例中,我们必须有一个 operator new ,它的第一个参数的类型为 size_t ,第二个参数的类型为 Memory_info :
void * operator new(size_t bytes, Memory_info info)
{
//……
}
某些 C++ 编译器会包含(在 new.h 中)一个特殊的 operator new ,它被用来指定被创建对象的存贮位置:
void * operator new(size_t, void * where)
{
return where;
}
上面的这种形式的 operator new 就可以强制将我们新创建的对象放置到某个特定的地方:
#include<new.h>
main()
{
/* 在地址 0x1234 处构建一个 Thing 对象 */
new ((void *)0x1234) Thing;
}
上面的代码会在地址 0x1234 处构建一个 Thing 对象。