转载请注明文章出处:tlanyan.me/construct-o…c++
为了提升程序的性能,一个作法是一次性分配足够多的内存,从而避免屡次申请以及数据拷贝。对于c++,有一个问题:如何在已分配好的内存上构造对象?函数
前文“vector的性能利器:reserve”提到使用reserve
预先分配内存,再push_back
或emplace_back
,存储过万个大对象时可极大提高效率。探究其实现原理,会发现分配内存简单,调用标准库或者nedmalloc
、tcmalloc
等库中的函数便可;有了内存,问题一样变成如何在已分配的内存上构造对象?性能
有两种解决方案解决这个问题。spa
第一种方案是使用placement new
。其用法过程为:首先分配足够大的内存;而后用placement new
语法生成对象:new(ptr) xxx()
,其中ptr
是足够容纳所指对象的指针。指针
一个使用例子:code
class Person {
private:
int age;
std::string name;
public:
// methods
};
int main(int argc, char** argv) {
char mem[sizeof(Person)]; // 或者 auto mem = malloc(sizeof(Person));
auto p = new(mem) Person();
assert((void*)p == (void*)mem); // 两个指针指向同一块内存
return 0;
}
复制代码
使用placement new
有三个注意点:一是要有足够的内存放置对象,这是必须的;二是指针应该是“对齐”的,例如对于4字节对齐的系统,指针地址应该是4的整数倍;三是你(可能)须要显式调用析构函数完成对象的销毁。对象
使用new
生成对象实际上执行了三个操做:内存
operator new
分配内存其中operator new
是可重载的,不管全局仍是特定类。其函数原型为:get
void* operator new(size_t sz);
复制代码
回到把对象在指定内存上构造的问题上,咱们能够经过重载operator new
,返回已分配内存的指针。然而因为operator new
函数只接受一个参数,地址指针须要是“全局”变量才能生效。这样想来,这种方案实用性并不高。原型
若是你但愿像vector中的reserve
先分配内存,而后在其上装载对象,可使用allocator
。allocator
定义在头文件中,能对指定类型分配合适的内存,并可手动调用对象的构造函数和析构函数。
用法示例:
int main(int argc, char** argv) {
std::allocator<Person> alloc;
auto p = alloc.allocate(1); // 分配一个Person对象的内存
alloc.construct(p); // 调用Person的构造函数,若是构造函数有参数,参数写在p以后
// p 如今是一个指向Person的指针,且其指向对象被初始化过
// 对p进行一些操做
// 销毁对象,但不释放内存,等同于调用p->~Person()
alloc.destroy(p);
// 释放内存
alloc.deallocate(p, 1);
return 0;
}
复制代码
对于能够内部管理的情形,建议使用allocator
而非placement new
。
为何有这个需求呢?我的以为有三方面的缘由:
reserve
,预先分配内存可大幅提升性能;