2012-07-01 36 views
8

我知道這是用「儲備」,以避免不必要的重新分配(有效STL的第14項)的好做法:在調用vector :: assign()之前調用vector :: reserve()會更好嗎?

std::vector<int> v1; 
v1.reserve(1000); 

for (int i = 0; i < 1000; i++) 
    v1.push_back(i); 

請問,當你調用分配相同的規則適用?

std::vector<int> v2; 
//v2.reserve(v1.size()); // Better to do this? 
v2.assign(v1.begin(), v1.end()); 
+0

我不知道爲什麼它不會保留正確的內存本身的蝙蝠,使用'的std :: distance'。 – chris

回答

6

如果當v1std::vector你並不真的需要它,因爲編譯器/ STL知道有多少項目是如何去那裏的v2(將reserve複製實際數據之前所需要的量本身)。

但是,對於一般情況,如果輸入容器(v1)不知道有多少物品在那裏,並且手頭有號碼,則可能需要預先提供所需數量的reserve

+0

我的代碼中存在拼寫錯誤。我打算寫v2.reserve(v1.size())。 – jpen

+0

我不確定你的意思。通過「萬一矢量v1你不需要它」,你是說v1.reserve(1000)不需要? – jpen

+0

@jpen:是的,因爲'assign'會自己做。 (這是針對'v1'是矢量的情況。)剛剛編輯答案以避免含糊不清。 – Vlad

6

是否撥打電話reserve與否取決於:

  • 迭代器類型:用於輸入迭代器的實現不能猜大小
  • 庫的質量:它可能並不是專門爲「好」的迭代器
  • 性能是否值得可維護性減少

讓我們以3點進行排序。

1)迭代器類型

assign方法採用兩個迭代器必須至少符合InputIterator模型。問題是這個模型代表純粹的來源(比如來自網絡的字節):你可以消耗兩倍的東西。因此,如果給定兩個InputIterator,則不可能在不提取數據的情況下計算它們之間的距離(除非根本不需要數據,但這不是指定的數據),因此您無法先「預留」 。

這通過std::distance至少需要來說明。

2)執行

我不認爲該標準強制要求實際爲「更好的」迭代器(其中至少模型ForwardIterator)認爲assign實施步行範圍兩倍的質量。在受內存帶寬限制的計算中(想象一下在磁帶上讀取這些信息以及非常緩慢的倒帶時間),這實際上會更加昂貴。

然而,許多實施方式(諸如的libC++,見下文)將專門assign使得在ForwardIterator存在它調用std::distance第一如有必要保留的存儲器的適量。

注意:同樣適用於大規模插入的方式。

3)維護負擔

我想指出的是,儘管可能的增益,你(也許在不知不覺中)在這裏重複的信息。

size_t size = std::distance(begin, end); 

if (begin != end) ++begin; // new line 

v.reserve(size); 
v.assign(begin, end); 

看看新行的外觀如何突然讓代碼稍微不正確?並不是說它不會起作用,但是所謂的優化不再那麼正確:現在你保留太多了!

個人而言,我會信任我的標準庫實現來做正確的事情。寫他們的人比我有更多的經驗。

如果真的它是您的應用程序中確定的瓶頸,您可以隨時嘗試。只需編寫一個reserve_and_assign方法來明確它的作用,並衡量它是否更好。


供參考,在這裏的libC++實現,taken here

template <class _Tp, class _Allocator> 
template <class _InputIterator> 
typename enable_if 
< 
    __is_input_iterator <_InputIterator>::value && 
    !__is_forward_iterator<_InputIterator>::value, 
    void 
>::type 
vector<_Tp, _Allocator>::assign(_InputIterator __first, _InputIterator __last) 
{ 
    clear(); 
    for (; __first != __last; ++__first) 
     push_back(*__first); 
} 

template <class _Tp, class _Allocator> 
template <class _ForwardIterator> 
typename enable_if 
< 
    __is_forward_iterator<_ForwardIterator>::value, 
    void 
>::type 
vector<_Tp, _Allocator>::assign(_ForwardIterator __first, _ForwardIterator __last) 
{ 
    typename iterator_traits<_ForwardIterator>::difference_type __new_size = _VSTD::distance(__first, __last); 
    if (static_cast<size_type>(__new_size) <= capacity()) 
    { 
     _ForwardIterator __mid = __last; 
     bool __growing = false; 
     if (static_cast<size_type>(__new_size) > size()) 
     { 
      __growing = true; 
      __mid = __first; 
      _VSTD::advance(__mid, size()); 
     } 
     pointer __m = _VSTD::copy(__first, __mid, this->__begin_); 
     if (__growing) 
      __construct_at_end(__mid, __last); 
     else 
      this->__destruct_at_end(__m); 
    } 
    else 
    { 
     deallocate(); 
     allocate(__recommend(static_cast<size_type>(__new_size))); 
     __construct_at_end(__first, __last); 
    } 
} 
相關問題