C++ 指向索引处向量的指针Vs迭代器

C++ 指向索引处向量的指针Vs迭代器,c++,pointers,stl,iterator,C++,Pointers,Stl,Iterator,我有一个向量myvec,我在代码中使用它来保存内存中的对象列表。我使用 Object* pObj = &myvec[index]; 如果。。。myvec没有足够大,以至于它在推回过程中移动,此时pObj无效-向量保证数据是连续的,因此它们不努力将向量保持在相同的内存位置 我可以为myvec保留足够的空间来防止这种情况,但我不喜欢这种解决方案 我可以保留所选myvec位置的索引,当我需要使用它时,只需直接访问它,但这是对我的代码进行的代价高昂的修改 我想知道迭代器是否在重新分配/移动向量

我有一个向量myvec,我在代码中使用它来保存内存中的对象列表。我使用

Object* pObj = &myvec[index];
如果。。。myvec没有足够大,以至于它在推回过程中移动,此时pObj无效-向量保证数据是连续的,因此它们不努力将向量保持在相同的内存位置

我可以为myvec保留足够的空间来防止这种情况,但我不喜欢这种解决方案

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

我想知道迭代器是否在重新分配/移动向量时保持其引用的完整性,如果是的话,我可以替换它吗

Object* pObj = &myvec[index];
大概是

vector<Object>::iterator = myvec.begin()+index;
vector::iterator=myvec.begin()+索引;
这意味着什么

这可行吗

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


干杯

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


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

否。。。使用迭代器也会遇到同样的问题。如果执行向量重新分配,则所有迭代器都将无效,并且使用它们是未定义的行为

使用
std::vector
的唯一解决方案是使用整数索引

例如,使用
std::list
事情是不同的,但效率也不同,所以这实际上取决于您需要做什么

另一个选项是创建您自己的“智能索引”类,该类存储对向量和索引的引用。通过这种方式,您可以只传递一个“指针”(您可以为它实现指针语义),但代码不会遭受重新分配风险。

迭代器(可能)会因任何可能调整向量大小的因素而失效(例如,推回)

但是,您可以创建自己的迭代器类来存储向量和索引,这在调整向量大小的操作中是稳定的:

#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
#包括
#包括
#包括
#包括
命名空间稳定{
模板
类迭代器:public std::iterator
{
T&container;
尺寸指数;
公众:
迭代器(T&container,size\u T index):容器(container),索引(index){
迭代器运算符+++(){++index_u2;返回*this;}
迭代器运算符++(int){iterator temp(*this);++index_u2;return temp;}
迭代器运算符--(){--index_u2;return*this;}
迭代器运算符--(int){stable_itertor temp(*this);--index_u;return temp;}
迭代器运算符+(Dist offset){返回迭代器(container,index_uu+offset);}
迭代器运算符-(Dist offset){返回迭代器(容器,索引u-offset);}
布尔运算符!=(迭代器常量和其他)常量{return index_!=other.index_;}
bool运算符==(迭代器const&other)const{return index\==other.index\}
布尔运算符(迭代器const&other)const{return index_>other.index_;}
typename T::value_type&operator*(){return container_u[index_u];}
typename T::value_type&operator[](size_T index){return container_u[index_u+index];}
};
模板
迭代器begin(T&container){返回迭代器(container,0);}
模板
迭代器结束(T&container){返回迭代器(container,container.size());}
}
#ifdef试验
int main(){
std::矢量数据;
//向容器中添加一些数据:

对于(inti=0;iTo add),如果您希望保留迭代器,可以查看列表?或
std::deque
,这取决于您对它所做的操作,保留索引是“昂贵的”?Meh。保留迭代器的标准模式是预先保留空间,或使用保留迭代器的容器。这两种方法都有折衷之处。您在下面说,您“使用向量就像使用数组”,这正是你应该做的——但我怀疑你的整个问题是一些糟糕的设计决策的结果。不知何故,这根本不应该是一个问题……不知道上下文就很难说。从什么意义上说,使用索引是“代价高昂的修改”“?您的意思是要更改的行太多了吗?还是
myvec[i]
的运行时成本大于
*pObj
的运行时成本?问题是列表没有[]运算符,这很糟糕,因为我使用向量就像使用数组一样。@Andre:
std::deque
只有在中间插入/删除时才会使迭代器无效。
push_back
pop_back
不会无效。它们有
[]
和查找速度更接近
vector
而不是
list
@MooingDuck。好吧,这似乎是应该走的路。感谢您的帮助;)@MooingDuck:From 23.3.3.4[deque.modifiers]/p1:在deque的两端插入一个命令会使所有到deque的迭代器失效。同一节中的p4声明
pop\u back()
(或删除最后一个元素)将使结束迭代器无效。@MooingDuck正如Howard所指出的,关于迭代器无效的部分是不正确的,但将“迭代器”替换为“引用(和指针)”,它就正确了。