C++在指定內存構造對象

轉載請註明文章出處:tlanyan.me/construct-o…c++

問題

爲了提升程序的性能,一個作法是一次性分配足夠多的內存,從而避免屢次申請以及數據拷貝。對於c++,有一個問題:如何在已分配好的內存上構造對象?函數

前文「vector的性能利器:reserve」提到使用reserve預先分配內存,再push_backemplace_back,存儲過萬個大對象時可極大提高效率。探究其實現原理,會發現分配內存簡單,調用標準庫或者nedmalloctcmalloc等庫中的函數便可;有了內存,問題一樣變成如何在已分配的內存上構造對象?性能

方案

有兩種解決方案解決這個問題。spa

placement new

第一種方案是使用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的整數倍;三是你(可能)須要顯式調用析構函數完成對象的銷燬。對象

operator new

使用new生成對象實際上執行了三個操做:內存

  1. 調用operator new分配內存
  2. 調用類的構造函數
  3. 返回指針

其中operator new是可重載的,不管全局仍是特定類。其函數原型爲:get

void* operator new(size_t sz);
複製代碼

回到把對象在指定內存上構造的問題上,咱們能夠經過重載operator new,返回已分配內存的指針。然而因爲operator new函數只接受一個參數,地址指針須要是「全局」變量才能生效。這樣想來,這種方案實用性並不高。原型

其餘

若是你但願像vector中的reserve先分配內存,而後在其上裝載對象,可使用allocatorallocator定義在頭文件中,能對指定類型分配合適的內存,並可手動調用對象的構造函數和析構函數。

用法示例:

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

做用

爲何有這個需求呢?我的以爲有三方面的緣由:

  1. 像vector的reserve,預先分配內存可大幅提升性能;
  2. 重複利用已分配好的空間,避免內存碎片;
  3. 細粒度進行內存管理,例如可以實現許多虛擬機中的將內存數據從一個片區轉移到另外一個片區(垃圾回收時觸發)。

參考

  1. stackoverflow.com/questions/5…
  2. isocpp.org/wiki/faq/dt…
相關文章
相關標籤/搜索