2015-01-01 78 views
2

我有一個T*尋址lenT類型的元素的緩衝區。由於某些原因,我需要以std::vector<T>的形式提供這些數據。據我所知,我不能構建一個使用我的緩衝區作爲內部存儲的向量。這是爲什麼?爲什麼我不能在一個std :: vector <T>中包裝一個T *?

注:

  • 請不要建議我使用迭代 - 我知道這通常是圍繞此類問題的方式。
  • 我不介意如果以後調整大小,矢量不得不復制數據。
  • 這個問題特別讓我感到困惑,因爲C++已經移動了語義。如果我們可以從它的腳下拉出一個物體的存儲器,爲什麼不能自己推動呢?
+0

爲什麼不直接將元素順序移動到矢量中?想必你把它們放到一個矢量中,以便以後可以調整集合的大小,所以你預計它們會被移動,對嗎?如果無論如何會發生這種情況,可能會多次,那麼避免它發生一次可能是一個不成熟的優化。 –

+0

@克里斯貝克:不,這不是我把它們放在向量中的原因。假設我有一些STLish代碼需要一個向量,而不是一個原始數組。 – einpoklum

+0

我想你不能使用'std :: array'或者是因爲動態調整大小或者是可能的?也許在你的原始緩衝區中有某種類型的包裝類型,它具有類似於STL的接口,足以滿足你的需要。我的猜測是,從頭開始做這樣的事情可能更容易,然後搞亂分配器 –

回答

11

您可以。

你寫了std::vector<T>,但std::vector需要兩個模板參數,不只是一個。第二個模板參數指定要使用的分配器類型,並且構造函數的重載允許傳入該分配器類型的自定義實例。

因此,您只需編寫一個分配器,儘可能使用您自己的內部緩衝區,並在您自己的內部緩衝區已滿時回退到詢問默認分配器。

默認分配器不可能希望處理它,因爲它不知道哪些內存位可以被釋放,哪些不能。


樣品狀態分配器與含有已構建的元件不應該由該載體被覆蓋,包括一個大的疑難雜症的示範內部緩衝器:

struct my_allocator_state { 
    void *buf; 
    std::size_t len; 
    bool bufused; 
    const std::type_info *type; 
}; 

template <typename T> 
struct my_allocator { 
    typedef T value_type; 

    my_allocator(T *buf, std::size_t len) 
     : def(), state(std::make_shared<my_allocator_state, my_allocator_state>({ buf, len, false, &typeid(T) })) { } 

    template <std::size_t N> 
    my_allocator(T(&buf)[N]) 
     : def(), state(std::make_shared<my_allocator_state, my_allocator_state>({ buf, N, false, &typeid(T) })) { } 

    template <typename U> 
    friend struct my_allocator; 

    template <typename U> 
    my_allocator(my_allocator<U> other) 
     : def(), state(other.state) { } 

    T *allocate(std::size_t n) 
    { 
     if (!state->bufused && n == state->len && typeid(T) == *state->type) 
     { 
      state->bufused = true; 
      return static_cast<T *>(state->buf); 
     } 
     else 
      return def.allocate(n); 
    } 

    void deallocate(T *p, std::size_t n) 
    { 
     if (p == state->buf) 
      state->bufused = false; 
     else 
      def.deallocate(p, n); 
    } 

    template <typename...Args> 
    void construct(T *c, Args... args) 
    { 
     if (!in_buffer(c)) 
      def.construct(c, std::forward<Args>(args)...); 
    } 

    void destroy(T *c) 
    { 
     if (!in_buffer(c)) 
      def.destroy(c); 
    } 

    friend bool operator==(const my_allocator &a, const my_allocator &b) { 
     return a.state == b.state; 
    } 

    friend bool operator!=(const my_allocator &a, const my_allocator &b) { 
     return a.state != b.state; 
    } 

private: 
    std::allocator<T> def; 
    std::shared_ptr<my_allocator_state> state; 

    bool in_buffer(T *p) { 
     return *state->type == typeid(T) 
      && points_into_buffer(p, static_cast<T *>(state->buf), state->len); 
    } 
}; 

int main() 
{ 
    int buf [] = { 1, 2, 3, 4 }; 
    std::vector<int, my_allocator<int>> v(sizeof buf/sizeof *buf, {}, buf); 
    v.resize(3); 
    v.push_back(5); 
    v.push_back(6); 
    for (auto &i : v) std::cout << i << std::endl; 
} 

輸出:

 
1 
2 
3 
4 
6 

5push_back適合舊的緩衝區,所以建築被繞過。當添加6時,將分配新內存,並且所有內容都按照正常方式開始工作。你可以通過向你的分配器添加一個方法來避免這個問題,以表明從那時起,建設不應該被繞過。

points_into_buffer原來是最難寫的部分,我從我的回答中忽略了這一點。預期的語義應該從我如何使用它中顯而易見。請參閱my question here以獲得便攜式實現,或者如果您的實現允許,請使用其他問題中較簡單的版本之一。順便說一下,我不是很滿意一些實現如何使用rebind這樣的方式,以避免存儲運行時類型信息以及狀態,但是如果你的實現不需要這樣做,那麼你可以通過使狀態成爲模板類(或嵌套類)來使其更簡單一些。

+0

這樣的分配器可以作爲標準庫的一部分嗎? – einpoklum

+0

@einpoklum Boost確實有一些自定義分配器,但我不知道任何具有您要查找的特定行爲。 – hvd

+1

這項工作?我們想要混合賦值/初始化和重用緩衝區(而不是無意義的自身拷貝)。 – Yakk

6

簡短的回答是,矢量不能使用你的緩衝區,因爲它不是這樣設計的。

它也是有意義的。如果一個向量沒有分配它自己的內存,當添加更多的項目時它如何調整緩衝區的大小?它分配一個新的緩衝區,但它對舊的有什麼影響?同樣適用於移動 - 如果矢量不控制自己的緩衝區,它如何控制這個緩衝區到另一個實例?

+0

zmbq:不,沒有意義。首先,假設它是一個const向量;它永遠不會調整。其次,即使它是可變的,調整大小也會使緩衝區重新分配成爲「公平遊戲」,並且你沒有得到任何承諾。可以提出一個類似的說法。 – einpoklum

+1

那麼,如果它不知道如何得到它,那麼如何重新分配你的原始'T *'?答案 - 它不能。並且破解分配器來跟蹤這個仍然沒有給你任何方式通知載體緩衝區已經被填充。 – Useless

相關問題