2015-04-04 58 views
1

假設我想要一個可變大小的數組,其中有一個標頭的位存儲在std::shared_ptr中。我可以做類似shared_ptr中的可變大小對象

#include <memory> 
using namespace std; 
struct obj { 
    char headerCode; 
    unique_ptr<short[]> data; 
}; 
shared_ptr<obj> make(unsigned len) { 
    return shared_ptr<obj>{new obj{'x', unique_ptr<short[]>{new short[len]}}}; 
} 

但是,這會導致三種分配:一個用於shared_ptr控制塊,一爲obj和一個用於其data。隨着make_shared有可能是前兩個共享一些內存:

#include <memory> 
using namespace std; 
struct obj { 
    char headerCode; 
    unique_ptr<short[]> data; 
    obj(char headerCode, short data[]) : headerCode(headerCode), data(data) {} 
}; 
shared_ptr<obj> make(unsigned len) { 
    return make_shared<obj>('x', new short[len]); 
} 

與低級別的分配,我可以讓對象及其數據共享一些內存:

#include <memory> 
#include <cstdlib> 
using namespace std; 
struct obj { 
    char headerCode; 
    short data[0]; 
}; 
shared_ptr<obj> make(unsigned len) { 
    obj* o = reinterpret_cast<obj*>(malloc(sizeof(obj) + len*sizeof(short))); 
    o->headerCode = 'x'; 
    return shared_ptr<obj>(o, free); 
} 

是通過允許這兩種技術標準?如果沒有,是否有類似的東西是允許的?有沒有什麼能夠以符合標準的方式使這項工作只有一個內存分配?最好不必在每個實例中存儲分配器或刪除器對象?

+0

爲什麼使用'reinterpret_cast'將'void *'轉換爲'obj *'? – 2015-04-04 23:22:05

+0

@JonathanWakely:沒有明確地施放我的'g ++'說'錯誤:從'void *'無效轉換爲'obj *'[-fpermissive]'。但是你說得對,這裏'static_cast'可能更合適。 – MvG 2015-04-04 23:26:59

+0

是的,當然它不會隱式轉換,但'reinterpret_cast'是一個大錘,在這裏沒有必要。 'static_cast'是所有需要的。 – 2015-04-04 23:28:02

回答

1

Are these two techniques allowed by the standard?

第一個是細,第二個使用一個零長度數組作爲構件(data[0]),它是無效的C++,但被支持爲一些編譯器的擴展。

Is there something which makes this work in a standards-conforming way with only a single memory allocation?

目前,它是相當困難的,而不http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3641.html這將允許這樣的事情:

struct obj { 
    char headerCode; 
    short* data; 
}; 

shared_ptr<obj> make(unsigned len) { 
    auto p = make_shared<char[]>(sizeof(obj) + sizeof(short)*len)); 
    short* s = static_cast<short*>(p.get() + sizeof(obj)); 
    return shared_ptr<obj>(p, ::new(p.get()) obj{'x', s}); 
} 

這種分配的char與用於objshort陣列足夠的空間陣列,然後使用放置新構建一個obj到該內存中,並創建另一個shared_ptr,該共享擁有擁有該char陣列的所有權。當擁有內存的最後一個shared_ptr刪除它的引用時,char數組將被正確刪除,但obj對象的析構函數將不會運行,所以重要的是不要依賴其具有任何副作用的析構函數(理想情況下它將具有一個微不足道的析構函數)。

您可能能夠利用今天與allocate_shared其中用於分配的short陣列額外的空間自定義分配器做到這一點,但因爲你不知道的實施將如何安排分配的控制模塊,並在空間的物體要計算short數組開始的位置並且需要將分配器存儲在控制塊中是相當困難的。

+1

即使不太可能導致「short」問題,也不要忘記考慮對齊。一般來說,不能保證'p.get()+ sizeof(obj)'適合於任何你想放在它之後的對象,儘管幾乎所有平臺都會使得指針與「short」相比具有同等嚴格或更嚴格的對齊要求'。 – hvd 2015-04-04 23:45:00

+0

是的,我確實考慮過它,但在這種情況下它應該是安全的,因爲尾隨指針成員應該對齊,以便「短」可以立即跟隨它。一般來說,你可能需要在分配時添加(最多)'alignof(T)'字節,並使用'std :: align'正確地放置一個'T'數組。 – 2015-04-04 23:47:01