2011-10-31 158 views
3

我有一個矢量<對象> myvec我在代碼中使用它來保存內存中的對象列表。我把一個指向該矢量當前對象的「正常」 Ç方式使用指向矢量指針Vs的指針迭代器

Object* pObj = &myvec[index]; 

這一切工作正常,如果...... myvec會不會種植足夠大,它是在到處移動push_back此時pObj變得無效 - 向量保證數據是連續的,因此它們不會將該向量保持在相同的存儲位置。

我可以預留myvec足夠的空間來防止這種情況,但我不喜歡這種解決方案。

我可以保留所選myvec位置的索引,當我需要使用它時,只需直接訪問它,但對我的代碼進行昂貴的修改。

如果作爲載體被重新分配迭代器保持其完整引用我不知道/移動如果是的話我能不能代替

Object* pObj = &myvec[index]; 

通過類似

vector<Object>::iterator = myvec.begin()+index; 

什麼含義這個的?

這是可行的嗎?

保存指向矢量位置的指針的標準模式是什麼?

Cheers

+0

保持索引是「昂貴的」?咩。 – akappa

+0

保留迭代器的標準模式是預先保留空間,或使用保留迭代器的容器。無論哪種方式都有妥協。 –

+0

你在下面說你「幾乎像數組一樣使用向量」,這正是你應該做的 - 但是我懷疑你的整個問題是一些糟糕的設計決定的結果。不知何故,這應該不成問題......只是在不瞭解背景的情況下很難說清楚。 –

回答

3

不......使用迭代器會產生同樣的問題。如果執行向量重新分配,則所有迭代器均失效並使用它們爲未定義行爲。

std::vector重新分配抵抗的唯一解決方案是使用整數索引。

使用例如std::list事情是不同的,但也是不同的效率妥協,所以它真的取決於你需要做什麼。

另一種選擇是創建您自己的「智能索引」類,它存儲對向量和索引的引用。這樣你就可以繼續傳遞一個「指針」(你可以實現指針的語義),但代碼不會遭受重新分配的風險。

+0

問題是列表沒有[]運算符,這很糟糕,因爲我使用向量幾乎作爲一個數組。 –

+2

@Andre:如果你在中間插入/刪除,'std :: deque'只會使迭代器無效。 'push_back'和'pop_back'不會失效。他們有'[]',查找速度比'list'更接近'vector'。 –

+0

@MooingDuck。那麼,這似乎是要走的路。感謝您的幫助;) –

2

不,迭代器在向量增長後失效。

解決此問題的方法是將索引保留爲該項目,而不是指向它的指針或迭代器。這是因爲即使向量增長,項目仍停留在其索引處,假設當然不會在其之前插入任何項目(從而更改其索引)。

+1

要添加,如果你想保持迭代器,也許看看列表? – paul23

+0

或'std :: deque',取決於你用它做什麼 –

3

迭代器(可能)會被可能調整向量大小的任何事情(例如push_back)無效化。

你可以,但是,創建一個存儲的向量指數,這將是跨調整向量運算穩定自己的迭代器類:

#include <iterator> 
#include <algorithm> 
#include <iostream> 
#include <vector> 

namespace stable { 

template <class T, class Dist=ptrdiff_t, class Ptr = T*, class Ref = T&> 
class iterator : public std::iterator<std::random_access_iterator_tag, T, Dist, Ptr, Ref> 
{ 
    T &container_; 
    size_t index_; 
public: 
    iterator(T &container, size_t index) : container_(container), index_(index) {} 

    iterator operator++() { ++index_; return *this; } 
    iterator operator++(int) { iterator temp(*this); ++index_; return temp; } 
    iterator operator--() { --index_; return *this; } 
    iterator operator--(int) { stable_itertor temp(*this); --index_; return temp; } 
    iterator operator+(Dist offset) { return iterator(container_, index_ + offset); } 
    iterator operator-(Dist offset) { return iterator(container_, index_ - offset); } 

    bool operator!=(iterator const &other) const { return index_ != other.index_; } 
    bool operator==(iterator const &other) const { return index_ == other.index_; } 
    bool operator<(iterator const &other) const { return index_ < other.index_; } 
    bool operator>(iterator const &other) const { return index_ > other.index_; } 

    typename T::value_type &operator *() { return container_[index_]; } 
    typename T::value_type &operator[](size_t index) { return container_[index_ + index]; } 
}; 

template <class T> 
iterator<T> begin(T &container) { return iterator<T>(container, 0); } 

template <class T> 
iterator<T> end(T &container) { return iterator<T>(container, container.size()); } 

} 

#ifdef TEST 
int main() { 

    std::vector<int> data; 

    // add some data to the container: 
    for (int i=0; i<10; i++) 
     data.push_back(i); 

    // get iterators to the beginning/end: 
    stable::iterator<std::vector<int> > b = stable::begin(data); 
    stable::iterator<std::vector<int> > e = stable::end(data); 

    // add enough more data that the container will (probably) be resized: 
    for (int i=10; i<10000; i++) 
     data.push_back(i); 

    // Use the previously-obtained iterators: 
    std::copy(b, e, std::ostream_iterator<int>(std::cout, "\n")); 

    // These iterators also support most pointer-like operations: 
    std::cout << *(b+125) << "\n"; 
    std::cout << b[150] << "\n"; 

    return 0; 
} 
#endif 

既然我們不能嵌入此作爲一個像正常迭代器類一樣的容器內部的嵌套類,這需要稍微不同的語法來聲明/定義這種類型的對象;而不是通常的std::vector<int>::iterator whatever;,我們必須使用stable::iterator<std::vector<int> > whatever;。同樣,要獲得容器的開始,我們使用stable::begin(container)

有一個點可以是(至少在開始)有點令人驚訝的:當你獲得stable::end(container),得到您容器的端當時。如上面的測試代碼所示,如​​果您以後在容器中添加更多項目,則先前獲得的迭代器不是而是,它調整爲反映容器的新結束 - 它保留了它在獲得容器時的位置(即,那個時候的位置是這個容器的末端,但是沒有了)。