轉載請註明文章出處: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
,預先分配內存可大幅提升性能;