简体   繁体   English

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

[英]pointer to vector at index Vs iterator

I have a vector< Object > myvec which I use in my code to hold a list of objects in memory. 我有一个vector <Object> myvec ,我在我的代码中使用它来保存内存中的对象列表。 I keep a pointer to the current object in that vector in the "normal" C fashion by using 我通过使用以“正常” C方式保持指向该向量中当前对象的指针

Object* pObj = &myvec[index];

This all works fine if... myvec doesn't grow big enough that it is moved around during a push_back at which time pObj becomes invalid - vectors guarantee data is sequential, hence they make no effort to keep the vector at the same memory location. 这一切都工作正常如果... myvec不会变得足够大以至于它在push_back中被移动,此时pObj变得无效 - 向量保证数据是顺序的,因此他们不会努力将向量保持在相同的内存位置。

I can reserve enough space for myvec to prevent this, but I dnt' like that solution. 我可以为myvec保留足够的空间以防止这种情况,但我不喜欢那种解决方案。

I could keep the index of the selected myvec position and when I need to use it just access it directly, but it's a costly modification to my code. 我可以保留所选myvec位置的索引,当我需要使用它时直接访问它,但这对我的代码来说是一个代价高昂的修改。

I'm wondering if iterators keep the their references intact as a vector is reallocated/moved and if so can I just replace 我想知道迭代器是否保持其引用完整,因为向量被重新分配/移动,如果可以,我只需要替换

Object* pObj = &myvec[index];

by something like 通过类似的东西

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

What are the implication of this? 这有什么含义?

Is this doable? 这可行吗?

What is the standard pattern to save pointers to vector positions? 保存指向矢量位置指针的标准模式是什么?

Cheers 干杯

No... using an iterator you would have the same exact problem. 不...使用迭代器你会遇到同样的问题。 If a vector reallocation is performed then all iterators are invalidated and using them is Undefined Behavior. 如果执行向量重新分配,则所有迭代器都将失效,并使用它们是未定义行为。

The only solution that is reallocation-resistant with an std::vector is using the integer index. 使用std::vector重新分配的唯一解决方案是使用整数索引。

Using for example std::list things are different, but also the are different efficiency compromises, so it really depends on what you need to do. 使用例如std::list东西是不同的,但也有不同的效率妥协,所以它实际上取决于你需要做什么。

Another option would be to create your own "smart index" class, that stores a reference to the vector and the index. 另一种选择是创建自己的“智能索引”类,它存储对向量和索引的引用。 This way you could keep just passing around one "pointer" (and you could implement pointer semantic for it) but the code wouldn't suffer from reallocation risks. 这样你就可以保持只传递一个“指针”(你可以为它实现指针语义)但代码不会受到重新分配风险的影响。

Iterators are (potentially) invalidated by anything that could resize the vector (eg, push_back). 迭代器(可能)被可能调整向量大小的任何东西(例如,push_back)无效。

You could, however, create your own iterator class that stored the vector and an index, which would be stable across operations that resized the vector: 但是,您可以创建自己的迭代器类来存储向量索引,该索引在调整向量大小的操作中是稳定的:

#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

Since we can't embed this as a nested class inside of the container like a normal iterator class, this requires a slightly different syntax to declare/define an object of this type; 由于我们不能像普通迭代器类那样将它作为嵌套类嵌入到容器内部,因此需要稍微不同的语法来声明/定义此类型的对象; instead of the usual std::vector<int>::iterator whatever; 而不是通常的std::vector<int>::iterator whatever; , we have to use stable::iterator<std::vector<int> > whatever; ,我们必须使用stable::iterator<std::vector<int> > whatever; . Likewise, to obtain the beginning of a container, we use stable::begin(container) . 同样,为了获得容器的开头,我们使用stable::begin(container)

There is one point that may be a bit surprising (at least at first): when you obtain a stable::end(container) , that gets you the end of the container at that time . 有一点可能有点令人惊讶(至少在开始时):当你获得一个stable::end(container) ,它会让你在那个时候收到容器的末尾。 As shown in the test code above, if you later add more items to the container, the iterator your obtained previously is not adjusted to reflect the new end of the container -- it retains the position it had when you obtained it (ie, the position that was the end of the container at that time, but isn't any more). 如上面的测试代码所示,如​​果稍后向容器中添加更多项,则先前获得的迭代器不会被调整以反映容器的新端 - 它保留了获取它时的位置(即, 当时容器末端的位置,但不再是)。

No, iterators are invalidated after vector growth. 不,矢量增长后迭代器无效。

The way to get around this problem is to keep the index to the item, not a pointer or iterator to it. 解决这个问题的方法是保持索引到项目,而不是指针或迭代器。 This is because the item stays at its index, even if the vector grows, assuming of course that you don't insert any items before it (thus changing its index). 这是因为该项目保持其索引,即使向量增长,当然假设您没有在它之前插入任何项目(从而更改其索引)。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM