简体   繁体   English

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

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

Today, I was attempting to extract a subset of N elements from a vector of size M, where N < M. I realised that I did not need to create a new copy, only needed to modify the original, and moreover, could take simply the first N elements. 今天,我试图从大小为M的向量中提取N个元素的子集,其中N <M。我意识到我不需要创建新副本,只需要修改原始副本,并且只需简单地前N个元素。

After doing a few brief searches, there were many answers, the most attractive one being resize() which appears to truncate the vector down to length, and deal neatly with the memory issues of erasing the other elements. 经过几次简短的搜索后,有很多答案,其中最吸引人的是resize(),它似乎将向量截短了长度,并巧妙地处理了擦除其他元素的内存问题。

However, before I came across vector.resize(), I was trying to point the vector.end() to the N+1'th position. 但是,在遇到vector.resize()之前,我试图将vector.end()指向第N + 1个位置。 I knew this wouldn't work, but I wanted to try it regardless. 我知道这行不通,但是无论如何我都想尝试一下。 This would leave the other elements past the N'th position "stranded", and I believe (correct me if i'm wrong) this would be an example of a memory leak. 这会使其他元素超出第N个位置“搁浅”,我相信(如果我错了,请纠正我)这将是内存泄漏的一个示例。

On looking at the iterator validity on http://www.cplusplus.com/reference/vector/vector/resize/ , we see that if it shrinks, vector.end() stays the same. http://www.cplusplus.com/reference/vector/vector/resize/查看迭代器的有效性时,我们看到,如果迭代器有效,则vector.end()保持不变。 If it expands, vector.end() will move (albeit irrelevant to our case). 如果扩展,vector.end()将移动(尽管与我们的情况无关)。

This leads me to question, what is the underlying mechanic of vector.end()? 这使我提出疑问, vector.end()的基本机制是什么? Where does it lie in memory? 它在哪里记忆? It can be found incrementing an iterator pointing to the last element in the vector, eg auto iter = &vector.back(), iter++, but in memory, is this what happens? 可以发现增加指向向量中最后一个元素的迭代器,例如auto iter =&vector.back(),iter ++,但是在内存中,这会发生什么吗?

I can believe that at all times, what follows vector.begin() should be the first element, but on resize, it appears that vector.end() can lie elsewhere other than past the last element in the vector. 我可以相信,始终在vector.begin()之后的内容应该是第一个元素,但是在调整大小时,似乎vector.end()可以位于除向量中最后一个元素之外的其他位置。

For some reason, I can't seem to find the answer, but it sounds like a very basic computer science course would contain this information. 出于某种原因,我似乎找不到答案,但这听起来像是一门非常基础的计算机科学课程将包含此信息。 I suppose it is stl specific, as there are probably many implementations of a vector / list that all differ... 我想它是stl特有的,因为vector / list的许多实现可能都不同...

Sorry for the long post about a simple question! 抱歉,关于一个简单问题的冗长帖子!

you asked about "the underlying mechanic of vector.end()". 您问过“ vector.end()的基本机制”。 Well here is (a snippet of) an oversimplified vector that is easy to digest: 好吧,这是一个易于消化的过度简化的向量(摘要):

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_;
   }
};

Also see this question Can std::vector<T>::iterator simply be T*? 另请参阅此问题std :: vector <T> :: iterator可以简单地设为T *吗? for why T* is a perfectly valid iterator for std::vector<T> 为什么T*std::vector<T>的完美有效iterator


Now with this implementation in mind let's answer a few of your 现在考虑到此实现,让我们回答一些 misconceptions 误解 questions: 问题:

I was trying to point the vector.end() to the N+1'th position. 我试图将vector.end()指向第N + 1个位置。

This is not possible. 这是不可能的。 The end iterator is not something that is stored directly in the class. 最终迭代器不是直接存储在类中的东西。 As you can see it's a computation of the begging of the buffer plus the size (number of elements) of the container. 如您所见,它是对缓冲区的请求加上容器大小(元素数)的计算。 Moreover you cannot directly manipulate it. 而且,您不能直接操纵它。 The internal workings of the class make sure end() will return an iterator pointing to 1 past the last element in the buffer. 该类的内部工作方式确保end()将返回指向缓冲区最后一个元素之后的1的迭代器。 You cannot change this. 您无法更改。 What you can do is insert/remove elements from the container and the end() will reflect these new changes, but you cannot manipulate it directly. 您可以做的是从容器中插入/删除元素, end()将反映这些新更改,但是您不能直接对其进行操作。

and I believe (correct me if i'm wrong) this would be an example of a memory leak. 并且我相信(如果我错了,请纠正我)这将是内存泄漏的一个示例。

you are wrong. 你错了。 Even if you somehow make end point to something else that what is supposed to point, that wouldn't be a memory leak. 即使您以某种方式将end指向应该指向的其他地方,也不会造成内存泄漏。 A memory leak would be if you would lost any reference to the dynamically allocated internal buffer. 如果您丢失对动态分配的内部缓冲区的任何引用,就会发生内存泄漏。

The "end" of any contiguous container (like a vector or an array) is always one element beyond the last element of the container. 任何连续容器(例如向量或数组)的“结束”始终是容器最后一个元素之外的一个元素。

So for an array (or vector) of X elements the "end" is index X (remember that since indexes are zero-based the last index is X - 1). 因此,对于X个元素的数组(或向量),“结束”是索引X(请记住,由于索引是从零开始的,所以最后一个索引是X-1)。

This is very well illustrated in eg this vector::end reference . 例如, vector::end reference中对此进行了很好的说明。

If you shrink your vector, the last index will of course also change, meaning that the "end" will change as well. 如果缩小向量,则最后一个索引当然也会更改,这意味着“ end”也将更改。 If the end-iterator does not change, then it means you have saved it from before you shrank the vector, which will change the size and invalidate all iterators beyond the last element in the vector, including the end iterator. 如果最终迭代器没有更改,则意味着您在收缩向量之前已保存了该迭代器,这将更改大小, 并使除向量中最后一个元素之外的所有迭代器(包括最终迭代器) 无效

If you change the size of a vector, by adding new elements or by removing elements, then you must re-fetch the end iterator. 如果通过添加新元素或删除元素来更改向量的大小,则必须重新获取结束迭代器。 The existing iterator objects you have will not automatically be updated. 您拥有的现有迭代器对象将不会自动更新。

Usually the end isn't stored in an implementation of vector. 通常,结尾不存储在vector的实现中。 A vector stores: 向量存储:

  1. A pointer to the first element. 指向第一个元素的指针。 If you call begin(), this is what you get back. 如果调用begin(),这就是您要返回的内容。
  2. The size of the memory block that's been managed. 已管理的内存块的大小。 If you call capacity() you get back the number of elements that can fit in this allocated memory. 如果调用capacity(),则会获取可以容纳在此分配内存中的元素数。
  3. The number of elements that are in use. 正在使用的元素数。 These are elements that have been constructed and are in the first part of the memory block. 这些是已构造的元素,位于内存块的第一部分。 The rest of the memory is unused, but is available for new elements. 其余的内存未使用,但可用于新元素。 If the entire capacity gets filled, to add more elements the vector will allocate a larger block of memory and copy all the elements into that, and deallocate the original block. 如果整个容量已满,则要添加更多元素,向量将分配一个更大的内存块并将所有元素复制到该内存块中,并取消分配原始块。

When you call end() this returns begin() + size(). 当您调用end()时,将返回begin()+ size()。 So yes, end() is a pointer that points to one beyond the last element. 因此,是的,end()是一个指向最后一个元素之外的指针。

So the end() isn't a thing that you can move. 因此,end()不能移动。 You can only change it by adding or removing elements. 您只能通过添加或删除元素来更改它。

If you want to extract a number of elements 'N' you can do so by reading those from begin() to begin() + 'N'. 如果要提取多个元素“ N”,则可以通过读取从begin()到begin()+'N'的元素来提取。

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

Many stl algorithms take a pair of iterators for the begin and end of a range of elements you want to work with. 许多stl算法在要使用的一系列元素的开始和结束处都带有一对迭代器。 In your case, you can use vec.begin() and vec.begin() + n as the begin and end of the range you're interested in. 对于您的情况,可以将vec.begin()和vec.begin()+ n用作您感兴趣的范围的开始和结束。

If you want to throw away the elements after n, you can do vec.resize(n). 如果要丢弃n之后的元素,可以执行vec.resize(n)。 Then the vector will destruct elements you don't need. 然后,向量将破坏您不需要的元素。 It might not change the size of the memory block the vector manages, the vector might keep the memory around in case you add more elements again. 它可能不会更改向量管理的内存块的大小,如果再次添加更多元素,向量可能会保留内存。 That's an implementation detail of the vector class you're using. 这是您使用的向量类的实现细节。

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

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