繁体   English   中英

std :: vector :: end()迭代器如何在内存中工作?

[英]How does std::vector::end() iterator work in memory?

今天,我试图从大小为M的向量中提取N个元素的子集,其中N <M。我意识到我不需要创建新副本,只需要修改原始副本,并且只需简单地前N个元素。

经过几次简短的搜索后,有很多答案,其中最吸引人的是resize(),它似乎将向量截短了长度,并巧妙地处理了擦除其他元素的内存问题。

但是,在遇到vector.resize()之前,我试图将vector.end()指向第N + 1个位置。 我知道这行不通,但是无论如何我都想尝试一下。 这会使其他元素超出第N个位置“搁浅”,我相信(如果我错了,请纠正我)这将是内存泄漏的一个示例。

http://www.cplusplus.com/reference/vector/vector/resize/查看迭代器的有效性时,我们看到,如果迭代器有效,则vector.end()保持不变。 如果扩展,vector.end()将移动(尽管与我们的情况无关)。

这使我提出疑问, vector.end()的基本机制是什么? 它在哪里记忆? 可以发现增加指向向量中最后一个元素的迭代器,例如auto iter =&vector.back(),iter ++,但是在内存中,这会发生什么吗?

我可以相信,始终在vector.begin()之后的内容应该是第一个元素,但是在调整大小时,似乎vector.end()可以位于除向量中最后一个元素之外的其他位置。

出于某种原因,我似乎找不到答案,但这听起来像是一门非常基础的计算机科学课程将包含此信息。 我想它是stl特有的,因为vector / list的许多实现可能都不同...

抱歉,关于一个简单问题的冗长帖子!

您问过“ vector.end()的基本机制”。 好吧,这是一个易于消化的过度简化的向量(摘要):

template <class T>
class Simplified_vector
{
public:
    using interator = T*;
    using const_interator = const T*;

private:
   T* buffer_;
   std::size_t size_;
   std::size_t capacity_;

public:

   auto push_back(const T& val) -> void
   {
       if (size_ + 1 > capacity_)
       {
           // buffer increase logic
           //
           // this usually means allocation a new larger buffer
           // followed by coping/moving elements from the old to the new buffer
           // deleting the old buffer
           // and make `buffer_` point to the new buffer
           // (along with modifying `capacity_` to reflect the new buffer size)
           //
           // strong exception guarantee makes things a bit more complicated,
           // but this is the gist of it
       }

       buffer_[size_] = val;
       ++size_;
   }

   auto begin() const -> const_iterator
   {
       return buffer_;
   }

   auto begin() -> iterator
   {
       return buffer_;
   }

   auto end() const -> const_iterator
   {
       return buffer_ + size_;
   }

   auto end() -> iterator
   {
       return  buffer_ + size_;
   }
};

另请参阅此问题std :: vector <T> :: iterator可以简单地设为T *吗? 为什么T*std::vector<T>的完美有效iterator


现在考虑到此实现,让我们回答一些 误解 问题:

我试图将vector.end()指向第N + 1个位置。

这是不可能的。 最终迭代器不是直接存储在类中的东西。 如您所见,它是对缓冲区的请求加上容器大小(元素数)的计算。 而且,您不能直接操纵它。 该类的内部工作方式确保end()将返回指向缓冲区最后一个元素之后的1的迭代器。 您无法更改。 您可以做的是从容器中插入/删除元素, end()将反映这些新更改,但是您不能直接对其进行操作。

并且我相信(如果我错了,请纠正我)这将是内存泄漏的一个示例。

你错了。 即使您以某种方式将end指向应该指向的其他地方,也不会造成内存泄漏。 如果您丢失对动态分配的内部缓冲区的任何引用,就会发生内存泄漏。

任何连续容器(例如向量或数组)的“结束”始终是容器最后一个元素之外的一个元素。

因此,对于X个元素的数组(或向量),“结束”是索引X(请记住,由于索引是从零开始的,所以最后一个索引是X-1)。

例如, vector::end reference中对此进行了很好的说明。

如果缩小向量,则最后一个索引当然也会更改,这意味着“ end”也将更改。 如果最终迭代器没有更改,则意味着您在收缩向量之前已保存了该迭代器,这将更改大小, 并使除向量中最后一个元素之外的所有迭代器(包括最终迭代器) 无效

如果通过添加新元素或删除元素来更改向量的大小,则必须重新获取结束迭代器。 您拥有的现有迭代器对象将不会自动更新。

通常,结尾不存储在vector的实现中。 向量存储:

  1. 指向第一个元素的指针。 如果调用begin(),这就是您要返回的内容。
  2. 已管理的内存块的大小。 如果调用capacity(),则会获取可以容纳在此分配内存中的元素数。
  3. 正在使用的元素数。 这些是已构造的元素,位于内存块的第一部分。 其余的内存未使用,但可用于新元素。 如果整个容量已满,则要添加更多元素,向量将分配一个更大的内存块并将所有元素复制到该内存块中,并取消分配原始块。

当您调用end()时,将返回begin()+ size()。 因此,是的,end()是一个指向最后一个元素之外的指针。

因此,end()不能移动。 您只能通过添加或删除元素来更改它。

如果要提取多个元素“ N”,则可以通过读取从begin()到begin()+'N'的元素来提取。

for( var it = vec.begin(); it != begin() + n; ++it )
{
    // do something with the element (*it) here.
}

许多stl算法在要使用的一系列元素的开始和结束处都带有一对迭代器。 对于您的情况,可以将vec.begin()和vec.begin()+ n用作您感兴趣的范围的开始和结束。

如果要丢弃n之后的元素,可以执行vec.resize(n)。 然后,向量将破坏您不需要的元素。 它可能不会更改向量管理的内存块的大小,如果再次添加更多元素,向量可能会保留内存。 这是您使用的向量类的实现细节。

暂无
暂无

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

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