指向索引Vs迭代器的向量的指针

时间:2011-10-31 18:05:22

标签: c++ pointers stl iterator

我有一个向量<对象> myvec 我在我的代码中用来保存内存中的对象列表。我在" normal"中保留了指向该向量中当前对象的指针。 C 时尚使用

Object* pObj = &myvec[index];

如果... myvec在 push_back 期间不会变得足够大,此时 pObj 变得无效,这一切都运行良好 - 向量保证数据是顺序的,因此他们不会努力将向量保持在相同的存储位置。

我可以为myvec预留足够的空间以防止这种情况,但我并没有这样做。喜欢那个解决方案。

我可以保留所选myvec位置的索引,当我需要使用它时,只需直接访问它,但这对我的代码进行了昂贵的修改。

我想知道迭代器是否保持其引用完整,因为向量被重新分配/移动,如果可以,我只需要替换

Object* pObj = &myvec[index];

类似

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

这有什么含义?

这可行吗?

保存指向矢量位置指针的标准模式是什么?

干杯

3 个答案:

答案 0 :(得分:3)

不...使用迭代器你会遇到同样的问题。如果执行向量重新分配,则所有迭代器都将失效,并使用它们是Undefined Behavior。

使用std::vector重新分配的唯一解决方案是使用整数索引。

使用例如std::list事情是不同的,但也有不同的效率妥协,所以它实际上取决于你需要做什么。

另一种选择是创建自己的“智能索引”类,它存储对向量和索引的引用。这样你就可以保持只传递一个“指针”(你可以为它实现指针语义),但代码不会受到重新分配风险的影响。

答案 1 :(得分: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)时,它会让你在容器的末尾时间。如上面的测试代码所示,如​​果稍后向容器中添加更多项,则先前获得的迭代器进行调整以反映容器的新端 - 它保留了当时的位置。你获得了它(即当时 容器末尾的位置,但不再是这个位置)。

答案 2 :(得分:2)

不,迭代器在向量增长后无效。

解决此问题的方法是将索引保留在项目中,而不是指针或迭代器。这是因为即使向量增长,项仍然保持在其索引处,当然假设您没有在它之前插入任何项(从而改变其索引)。