2010-04-25 49 views
3

C++不允許含有項目的數組未缺省構造一個類:內聯在C++類的非缺省構造對象的數組

class Gordian { 
    public: 
    int member; 
    Gordian(int must_have_variable) : member(must_have_variable) {} 
}; 

class Knot { 
    Gordian* pointer_array[8]; // Sure, this works. 
    Gordian inlined_array[8]; // Won't compile. Can't be initialized. 
}; 

由於甚至開始C++用戶知道,語言保證所有非POD成員在構建課程時都被初始化。它不相信用戶初始化構造函數中的所有內容 - 在構造函數的主體甚至啓動之前,必須爲所有成員的構造函數提供有效的參數。

一般情況下,這是一個偉大的想法,據我擔心,但我已經遇到一種情況,那將是一個容易得多,如果我能實際上有非缺省構造的對象的數組。

顯而易見的解決方案:有指針指向對象的數組。在我使用共享內存的情況下,這不是最佳選擇。這會迫使我從已經存在爭用的資源(即共享內存)中進行額外分配。我希望在對象中內聯數組的原因是爲了減少分配數量。

這是一個情況,我願意用一個黑客,甚至是難看,只要它的作品。一個可能的黑客攻擊我的想法是:

class Knot { 
    public: 
    struct dummy { char padding[sizeof(Gordian)]; }; 
    dummy inlined_array[8]; 
    Gordian* get(int index) { 
     return reinterpret_cast<Gordian*>(&inlined_array[index]); 
    } 
    Knot() { 
     for (int x = 0; x != 8; x++) { 
     new (get(x)) Gordian(x*x); 
     } 
    } 
}; 

當然,它編譯,但我不太有經驗的C++程序員。也就是說,我不太可能相信我的黑客。所以,問題:

1)請問我想出了破解似乎可行?有什麼問題? (我主要關注新版GCC上的C++ 0x)。

2)是否有更好的方式來內聯非缺省構造的對象的類中的一個數組?

+0

該代碼是合法的。但是,我認爲將非POD C++對象放在共享內存中是一個糟糕的設計。 – 2010-04-25 11:12:26

+0

我認爲你現在的解決方案是可以的。你可能想使用自定義分配器,將來自您準備緩衝區分配內存'VECTOR',但這種做法據我所知,如果你不想預分配任何額外的內存在這個緩衝區不起作用。 – 2010-04-25 11:31:15

+0

聲明「的語言擔保,構建一個類時,所有成員都被初始化,」是不正確。 C++不保證初始化POD類型(int,C風格結構等)。 – 2010-04-25 12:15:44

回答

1

一方面,可以使用陣列的包裝(如boost::array)到陣列具有固定值初始化:

#include <boost/array.hpp> 

class Gordian { 
public: 
    int member; 
    Gordian(int must_have_variable) : member(must_have_variable) {} 
}; 

namespace detail 
{ 
    boost::array<Gordian, 8> chop() 
    { 
     boost::array<Gordian, 8> t = {{0, 1, 4, 9, 16, 25, 36, 49}}; 
     return t; 
    } 
} 

class Knot { 
    boost::array<Gordian, 8> array; 
public: 
    Knot(): array(detail::chop()) {} 
}; 

另一種可能性將是boost::optional的陣列(但會有一些大小的額外開銷):

#include <boost/optional.hpp> 

class Gordian { 
public: 
    int member; 
    Gordian(int must_have_variable) : member(must_have_variable) {} 
}; 

class Knot { 
    boost::optional<Gordian> array[8]; 
public: 
    Knot() 
    { 
     for (int x = 0; x != 8; x++) { 
      array[x] = Gordian(x*x); 
     } 
    } 
}; 
+0

任何人都可以告訴我爲什麼這個答案是downvoted?對我來說,這似乎是一個有趣的可能性。它有什麼問題嗎? – porgarmingduod 2010-04-25 12:35:32

+0

其實用升壓可選的將可能是最簡單的方法。幾個字節的開銷不是問題,尤其是當它使我免於調用共享內存分配器兩次時。 – porgarmingduod 2010-04-25 13:05:30

1

內存對齊可能會中斷,因爲Knot認爲它只包含字符。除此之外,訣竅是可行的。我已經看到了另一種更普遍的關鍵是要提供

SuperOverOptimisedVector<Gordian> v; 
... 
Gordian* g = v.append(); 
new (g) Gordian(42); 

外觀可以欺騙插入成員函數返回原始內存調用者來填充,例如:,所以我會解釋。 v.append()函數不會從堆中分配原始內存。它只是發現矢量中的下一個可用插槽(如果容量耗盡,調整大小和複製,與std :: vector的方式相同),並將該插槽的地址傳回給調用者。

這招,而可愛和聰明,有些奇怪的和容易出錯的。這可以通過堅持一個步驟的約定來部分緩解:

new (v.append()) Gordian(42); 

但我更願意把它當作一般應避免一個有趣的好奇心。

因此,在總結,是的,你可以存儲非默認constructible對象在一個連續的數組,但除非性能差異大到足以影響您的項目的成功,只是去的std ::向量。

1

根據我得到的答案和我最初的黑客,我想出了利用boost::aligned_storage這個通用的解決方案。基本上是一個空洞類型,但對於結構。

class Gordian { 
    public: 
    int member; 
    Gordian(int must_have_variable) : member(must_have_variable) {} 
}; 

template <class T> 
struct VoidWrap { 
    boost::aligned_storage<sizeof(T)> storage; 
    T* address() { return reinterpret_cast<T*>(storage.address()); } 
}; 

class Knot { 
    public: 
    VoidWrap<Gordian> void_gordian[8]; 
    Knot() { 
     for (int x = 0; x != 8; x++) { 
     new (void_gordian[x].address()) Gordian(x*x); 
     } 
    } 
}; 

或者,專門用於以下用例的擴展版本:1)在包含它的對象的構造器中進行初始化。 2)訪問。 3)可能重新分配值。 4)適當的自動銷燬。 (當然,如果它沒有被初始化就被破壞/訪問它可能會炸燬)

template <class T> 
struct VoidWrap { 
    boost::aligned_storage<sizeof(T)> storage; 
    /// Returns an address on which to construct the object. Use only once. 
    T* construct() { return access(); } 
    /// access should only be called on a `construct`ed object 
    /// obj.access() is equivalent to &obj 
    T* access() { return reinterpret_cast<T*>(this); } 
    /// Assumes the object has been constructed. Calls the destructor on the old 
    /// object, then returns an address on which to construct a new object. 
    T* reconstruct() { 
    access()->~T(); 
    return access(); 
    } 
    ~VoidWrap() { 
    access()->~T(); 
    } 
}; 

VoidWrap<Gordian> void_g; 
new (void_g.construct()) Gordian(10); 
cout << void_g.access()->member << endl; 
new (void_g.reconstruct()) Gordian(20);