简体   繁体   English

push_back 是如何在 STL 向量中实现的?

[英]how is push_back implemented in STL vector?

I was asked this question in an interview.我在一次采访中被问到这个问题。

The points I answered are like this我回答的要点是这样的

1) an index pointing to the current position; 1) 指向当前位置的索引;

2) resize if neccessary. 2) 必要时调整大小。

Can anybody elaborate more?有人可以详细说明吗?

An STL vector has a size (current number of stored elements) and a capacity (currently allocated storage space). STL vector具有size (当前存储元素的数量)和capacity (当前分配的存储空间)。

  • If size < capacity , a push_back simply puts the new element at the end and increments the size by 1.如果size < capacity ,则push_back只是将新元素放在末尾并将size增加 1。
  • If size == capacity before the push_back , a new, larger array is allocated (twice the size is common, but this is implementation-dependent afaik), all of the current data is copied over (including the new element), and the old allocated space is freed.如果在push_back之前size == capacity ,则分配一个新的更大的数组(大小的两倍是常见的,但这是依赖于实现的afaik),所有当前数据都被复制过来(包括新元素),并且旧的分配的空间被释放。 This may throw an exception if the allocation fails.如果分配失败,这可能会引发异常。

The complexity of the operation is amortized O(1), which means during a push_back that causes a resize, it won't be a constant-time operation (but in general over many operations, it is).操作的复杂性分摊为O(1),这意味着在导致调整大小的push_back期间,它不会是恒定时间操作(但通常在许多操作中是)。

template< typename T >
void std::vector<T>::push_back(const T& obj)
{
    this->insert(this->end(),obj);
}

That, of course, is inherently implementation defined.当然,这本质上是实现定义的。 Assuming it's a question of how somebody would implement a dynamic array, in general, it'd be something like this:假设这是一个关于某人如何实现动态数组的问题,一般来说,它会是这样的:

  1. push_back checks capacity() and ensures it's at least one larger than size() . push_back检查capacity()并确保它至少比size() push_back
  2. If there is no capacity for the new element, the vector reallocates it's entire underlying storage, copying over the contents of the old storage to the new storage.如果新元素没有容量,向量会重新分配它的整个底层存储,将旧存储的内容复制到新存储。 The old storage is deallocated.旧存储被解除分配。
  3. The new element is copied to the end of the dynamic array.新元素被复制到动态数组的末尾。

Some STL implementations will elide some of the copies by using swaps (ie for containers of containers), but for the most part that's exactly how it works.一些 STL 实现将通过使用交换(即对于容器的容器)来消除一些副本,但在大多数情况下,这正是它的工作原理。

Possibly what they were looking for is that push_back makes a copy of the object being pushed onto the vector (using its copy constructor).可能他们正在寻找的是push_back制作被推送到vector上的对象的副本(使用其复制构造函数)。

With regard to resizing: The standard says a.push_back(x) is equivalent to a.insert(a.end(),x) .关于调整大小:标准说a.push_back(x)等效于a.insert(a.end(),x) The definition of insert says, in part: “Causes reallocation if the new size is greater than the old capacity.” insert的定义部分说明:“如果新大小大于旧容量,则会导致重新分配。”

The standard says what the functions are supposed to do.标准说明了函数应该做什么。 But how they're implemented is, in most cases, implementation-specific.但是在大多数情况下,它们的实现方式是特定于实现的。

Like so:像这样:


void push_back(T const& param)
{
  vector temp(rbegin(), rend());
  temp.push_front(param);
  *this = vector(temp.rbegin(), temp.rend());
}

If size < capasity, then everything seems to be ok, you just insert en element in vector, the complexity accures when size == capasity.如果 size < capasity,那么一切似乎都没问题,您只需在 vector 中插入 en 元素,当 size == capasity 时复杂性会增加。

It is easy to explaint on words that you have to allocate a new array twice larger then existing one and copy all those elements in new alocated memory and after delete the old one and insert the new element in new array.用文字很容易解释,您必须分配一个比现有数组大两倍的新数组,并将所有这些元素复制到新分配的内存中,然后删除旧数组并将新元素插入新数组中。 But here are some key moments which I consider your interviewer expects you to mention.但这里有一些我认为你的面试官希望你提到的关键时刻。

  1. The elements in std::vector are not kept in one array, so the data member of std::vector[1000] is not a int* of lenght 1000. The elements are kept in blocks of memory, thus you must consider it when copying. std::vector 中的元素不保存在一个数组中,因此 std::vector[1000] 的数据成员不是长度为 1000 的 int*。元素保存在内存块中,因此您必须在使用时考虑它复制。

  2. Secondly, the standard stl vector requires "give me a copyable object and I can insert it", which means that requierment on std::vector is that sometype only has to have a copy_constructer (not operator = ).其次,标准 stl 向量需要“给我一个可复制的对象,我可以插入它”,这意味着对 std::vector 的要求是 sometype 只需要有一个 copy_constructer (而不是 operator = )。 So when you are alocating a new memory of type sometype, you must consider that sometyme may not have a default copy constructer.因此,当您分配 sometype 类型的新内存时,您必须考虑 sometyme 可能没有默认的复制构造函数。 In common implementations, this is done by placement new operator to avoid the call of copy constructors.在常见的实现中,这是通过放置 new 运算符来完成的,以避免调用复制构造函数。

Thanks to some comments, I am completely revising a very incorrect original answer.感谢一些评论,我正在完全修改一个非常不正确的原始答案。

According to the STL spec , your answer was correct.根据 STL 规范,您的答案是正确的。 The vector is implemented as a dynamically resized array:该向量被实现为一个动态调整大小的数组:

Vector containers are implemented as dynamic arrays;向量容器被实现为动态数组; Just as regular arrays, vector containers have their elements stored in contiguous storage locations, which means that their elements can be accessed not only using iterators but also using offsets on regular pointers to elements.就像常规数组一样,向量容器将它们的元素存储在连续的存储位置,这意味着它们的元素不仅可以使用迭代器访问,还可以使用指向元素的常规指针的偏移量来访问。

But unlike regular arrays, storage in vectors is handled automatically, allowing it to be expanded and contracted as needed.但与常规数组不同的是,向量中的存储是自动处理的,允许根据需要扩展和收缩。

vector doesn't use a linked list. vector不使用链表。 It uses continuous memory.它使用连续内存。

If there is not enough reserved space push_back allocates a new chunk of memory twice as large as the original vector .如果没有足够的保留空间, push_back分配一个两倍于原始vector的新内存块。 By doing that the amortized runtime is O(1).通过这样做,摊销的运行时间是 O(1)。

How much detail did the interviewer want?面试官想要多少细节? For example, was he looking for you to drill down into the lower level details?例如,他是否正在寻找您深入了解较低级别的细节?

Besides the usual resize-as-needed to retain the O(1) on average semantics, some things to consider include but are not limited to:除了通常的按需要调整大小以保留平均语义的 O(1) 之外,需要考虑的一些事项包括但不限于:

  • Exception safety : Does the implementation provide a guarantee that state of the vector will not be modified if an exception is thrown when appending the new element (eg rollback semantics)?异常安全:实现是否保证如果在附加新元素时抛出异常(例如回滚语义),向量的状态不会被修改? For example, an exception could be thrown during allocation, copying, insertion, etc.例如,在分配、复制、插入等过程中可能会抛出异常。
  • Proper use of allocator : Does the implementation correctly use the vector 's allocator instead of plain old new based allocator (both may or may not be the same)?正确使用分配器:实现是否正确使用了vector的分配器而不是普通的基于new的旧分配器(两者可能相同也可能不同)? Ideally, this will be handled transparently by the vector 's resizing code, implementations could certainly differ, however.理想情况下,这将由vector的调整大小代码透明地处理,但是实现肯定会有所不同。

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

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