std :: vector是否改变了它的地址?怎么避免

时间:2010-03-15 13:26:07

标签: c++ stl vector

由于向量元素是连续存储的,我猜它可能在一些push_back之后没有相同的地址,因为初始分配的空间是不够的。

我正在编写一个代码,我需要引用向量中的元素,例如:

int main(){
    vector<int> v;
    v.push_back(1);
    int *ptr = &v[0];
    for(int i=2; i<100; i++)
        v.push_back(i);
    cout << *ptr << endl; //?
    return 0;
}

ptr包含对v[0]的引用并不一定正确,对吧?怎样才能保证呢?

我的第一个想法是使用指针向量和动态分配。我想知道是否有更简单的方法吗?

PS:实际上我正在使用类的向量而不是int,但我认为问题是相同的。

8 个答案:

答案 0 :(得分:12)

不要使用保留推迟这个悬空指针错误 - 作为有同样问题的人,耸耸肩,保留1000,然后几个月后花了很多年时间试图找出一些奇怪的内存错误(矢量容量超过1000),我可以告诉你这不是一个强大的解决方案。

如果可能的话,您希望避免使用向量中的元素地址,因为重新分配的不可预测性。如果必须,使用迭代器而不是原始地址,因为检查过的STL实现会告诉您它们何时变为无效,而不是随机崩溃。

最佳解决方案是更换容器:

  • 你可以使用std :: list - 它在添加元素时不会使现有的迭代器无效,并且在擦除时只有擦除元素的迭代器无效
  • 如果您使用的是C ++ 0x,则std :: vector&lt; std :: unique_ptr&lt; T&gt;&gt;是一个有趣的解决方案
  • 或者,使用指针和new / delete也不错 - 只需不要忘记在删除之前删除指针。以这种方式做到这一点并不难,但你必须非常小心,不要忘记删除而导致内存泄漏。 (Mark Ransom还指出:这不是异常安全的,如果异常导致向量被破坏,则整个向量内容都会泄露。)
  • 请注意,某些STL算法无法安全地使用boost的ptr_vector,这对您来说可能是一个问题。

答案 1 :(得分:10)

您可以通过调用其reserve成员函数来增加向量使用的基础数组的容量:

v.reserve(100);

只要你没有在矢量中放置超过100个元素,ptr将指向第一个元素。

答案 2 :(得分:6)

  

怎样才能保证呢?

std::vector<T>保证是连续的,但是实现可以在更改向量内容的操作上自由重新分配或释放存储(向量迭代器,指针或对元素的引用也变得不确定)。

但是,您可以致电reserve来获得所需的结果。 IIRC,该标准保证在向量大小大于其保留容量之前不进行重新分配。

一般来说,我会小心它(你很快就会陷入困境......)。除非你真的需要,否则最好不要依赖std::vector<>::reserve和迭代器持久性。

答案 3 :(得分:5)

另一种可能的可能性是专用的智能指针,它不是存储地址而是存储矢量本身的地址以及你关心的项目的索引。然后它会把它们放在一起,只有当你取消引用它时才得到元素的地址,如下所示:

template <class T>
class vec_ptr { 
    std::vector<T> &v;
    size_t index;
public:
    vec_ptr(std::vector<T> &v, size_t index) : v(v), index(index) {}

    T &operator*() { return v[index]; }
};

然后您的int *ptr=&v[0];将替换为:vec_ptr<int> ptr(v,0);

有几点:首先,如果你在创建“指针”的时间和你取消引用它的时间之间重新排列向量中的项目,它将不再引用原始元素,而是引用任何元素碰巧在指定的位置。其次,这不进行范围检查,因此(例如)尝试在仅包含50个项目的向量中使用100 th 项将给出未定义的行为。

答案 4 :(得分:2)

正如James McNellis和Alexander Gessler所说,reserve是预先分配记忆的好方法。但是,为了完整起见,我想补充一点,为了使指针保持有效,所有插入/删除操作必须从向量的尾部发生,否则项目移位将再次使指针无效。

答案 5 :(得分:2)

如果您不需要连续存储值,则可以使用std :: deque而不是std :: vector。它没有重新分配,而是将元素保存在几个内存块中。

答案 6 :(得分:1)

根据您的要求和使用案例,您可能需要查看Boost's Pointer Container Library

在您的情况下,您可以使用boost::ptr_vector<yourClass>

答案 7 :(得分:1)

我也遇到了这个问题,花了一整天时间才发现矢量地址已经改变,保存的地址变得无效。对于我的问题,我的解决方案是那个

  1. 将原始数据保存在向量中并获取相对索引
  2. 在向量停止增长之后,将索引转换为指针地址
  3. 我找到了以下作品

    1. 指针[I] =指数[I] +(为size_t)及矢量[0];
    2. pointers [i] =&amp; vector [(size_t)indices [i]];
    3. 但是,我还没弄清楚如何使用vector.front(),我不确定是否应该使用 pointers [i] = indices [i] * sizeof(vector)+(size_t)&amp; vector [0]。我认为参考方式(2)应该是非常安全的。