【问题标题】:vector pointer locations guaranteed?保证向量指针位置?
【发布时间】:2012-02-02 23:59:17
【问题描述】:

假设我有一个整数向量,

std::vector<int> numbers;

填充了一堆值,然后我说这样做(在 43 处存在条目)

int *oneNumber = &numbers[43];

是否保证 oneNumber 始终指向索引 43 处的 int,即使说我将数字调整为 numbers.resize(46) 之类的东西?

我不能 100% 确定这里的预期行为是什么,我知道向量可以保证是连续的,但不确定这种连续性是否也意味着向量中的所有索引在其整个生命周期中都将保持在同一个位置。

【问题讨论】:

  • 出于这个原因,我总是对创建值类型的向量持谨慎态度,因为我不可避免地需要一个指向向量内对象的指针。所以我保存了一个参考,如int* ref = &amp;numbers[43].. 稍后将几个push_backs 放入numbers 和WHAMMO! Bug:ref 无效,因为numbers 已被完全重新分配并保存在完全不同的内存空间中。
  • @bobobobo 非常正确。尽管对于在创建时元素数量已知的情况(不,我不能使用std::array,因为我emplace-通过循环构造值),reserve 保证所有指针/迭代器都进入向量将保持有效,只要它的大小没有超出其当前容量(这可能会强制重新分配并因此移动包含值的地址)。这救了我几次,但每一次我都忘记reserve,然后挠头一段时间,直到我想起来。 :D

标签: c++ pointers vector


【解决方案1】:

oneNumber 是否保证始终指向索引 43 处的 int

是的,这是由标准保证的。

即使说我将数字调整为 numbers.resize(46) 之类的东西?

没有。一旦你调整、添加或删除向量的任何内容,它的所有地址和迭代器都将失效。这是因为向量可能需要重新分配新的内存位置。

【讨论】:

  • 如果您使用std::list 会怎样?如果您只使用list.push_back() 调用来添加项目,那会规避无效地址问题吗? (并且您没有删除oneNumber 指向的项目?
  • @bobobobo 这是个好问题。我不确定标准对此有何规定,但我认为添加/删除std::list 不会使任何指针无效。 (被移除的元素除外)We're discussing it in the lounge right now if you wanna join in.
  • 找到我目前正在使用的another workaround
【解决方案2】:

你的偏执是正确的。调整std::vector 的大小可能会导致其内存位置发生变化。这意味着您的 oneNumber 现在指向已释放的旧内存位置,因此访问它是未定义的行为。

【讨论】:

    【解决方案3】:

    指向std::vector 元素的指针、引用和迭代器保证保持不变,只要您只附加到std::vector 并且 std::vector 的大小不会超过获取指针、引用或迭代器时的capacity()。一旦它的大小超出capacity(),所有指向此std::vector 的指针、引用和迭代器都将失效。请注意,在插入 std::vector 末尾以外的其他位置时,事情也会失效。

    如果您想让您的对象保持原样,并且只在末尾或开头插入新元素,您可以使用std::dequestd::deque 中元素的指针和引用仅在您插入std::deque 的中间或从中间移除或移除引用的对象时才会失效。请注意,每次将元素插入std::deque 或从中删除任何元素时,std::deque 中元素的迭代器都会失效。

    【讨论】:

      【解决方案4】:

      正如所有其他人所说,当您在向量上调用 .resize() 时,您的指针会失效,因为(旧数组)可能会被完全释放,并且可能会重新分配一个全新的数组并将您的数据复制到其中.

      一种解决方法是不要将指针存储 到 STL 向量中。相反,存储整数索引

      所以在你的例子中,

      std::vector<int> numbers;
      int *oneNumber = &numbers[43]; // no. pointers invalidated after .resize or possibly .push_back.
      int oneNumberIndex = 43 ;      // yes. indices remain valid through .resize/.push_back
      

      【讨论】:

        【解决方案5】:

        否 - 向量在增长时可以重新分配。通常一旦向量的大小翻倍。

        来自 C++11 标准

        1 Remarks: Causes reallocation if the new size is greater than the old capacity. If no
        reallocation happens, all the iterators and references before the insertion point
        remain valid. If an exception is thrown other than by the copy constructor, move 
        constructor, assignment operator, or move assignment operator of T or by any 
        InputIterator operation there are no effects. If an exception is thrown by the move 
        constructor of a non-CopyInsertable T, the effects are unspecified.
        

        【讨论】:

          【解决方案6】:

          当您使用向量的 resize() 或 reserve() 函数来增加向量的容量时,它可能需要为数组支持重新分配内存。如果它确实重新分配,新的内存将不会位于相同的地址,因此存储在 oneNumber 中的地址将不再指向正确的位置。

          同样,这取决于向量当前用于存储多少元素以及请求的大小。根据具体情况,向量可能能够在不重新分配的情况下调整大小,但您绝对不应该假设会是这种情况。

          【讨论】:

            【解决方案7】:

            一旦你改变了向量的容量,数据就会被复制到另一个内存块,原始数据被删除。

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 2011-08-04
              • 2011-07-02
              • 1970-01-01
              • 2020-04-13
              • 2016-05-15
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多