2011-10-31 119 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),得到您容器的端当时。如上面的测试代码所示,如​​果您以后在容器中添加更多项目,则先前获得的迭代器不是而是,它调整为反映容器的新结束 - 它保留了它在获得容器时的位置(即,那个时候的位置是这个容器的末端,但是没有了)。