如何实现std::vector插入?c++

How is std::vector insert implemented? C++

本文关键字:vector 插入 c++ std 何实现 实现      更新时间:2023-10-16

最近我重读了ISO c++标准,发现了一个非常有趣的注释:

注意,对于std::vector, std::vector<T>T类型的唯一约束是T类型必须有复制构造函数。实际上,如果插入时vector的内存已满,则分配一个新的size = 2 * oldSize内存(这取决于实现),然后复制其中的旧元素并插入该元素。

但是等等? ?

要分配新的内存类型,我们需要这样的东西,ptr = new T[2*size];

    这是如何做到的,因为T类型可能没有默认构造函数?然后赋值,在分配内存之后,我们必须将旧值分配给新内存,对吗?
  1. 考虑到这2件事,std::vector如何做到这一点与"只复制构造函数?"使用了哪些实现和语言习语?

通过调用allocator函数allocate()获取原始内存,然后调用allocatorconstruct(iterator, val)通过使用位置new复制来构造一个元素,即类似于

:
/* approach similar to std::uninitialized fill taken */
template<typename T, typename A >
vector<T,A>::vector( size_type n, const T& val, const A& a) : alloc( a)  // copy the allocator
{
    /* keep track of which elements have been constructed
     * and destroy those and only those in case of exception */
    v = alloc.allocate( n); // get memory for elements
    iterator p;             // declared before try{} so it is still valid in catch{} block
    try {
        iterator end = v + n;
        for( p = v; p != end; ++p)
            alloc.construct( p, val); /* construct elements (placement new):
                                      e g. void construct( pointer p, const T& val) 
                                      { ::new((void *)p) T( val); } */
        last = space = p;
    } catch( ...) {
        for( iterator q = v; q != p; ++q)
            alloc.destroy( q);       /* destroy constructed elements */
        alloc.deallocate( v, n);     /* free memory */
        throw;                       /* re-throw to signal constructor that failed */
    }
}

在c++中,allocator用于将必须分配内存的算法实现者和容器与物理内存的细节隔离开来。

也可以采用直接使用uninitialized_fill的方法:

 std::uninitialized_fill( v, v + n, val); /* copy elements with (placement new):
                                             e g. void construct( pointer p,
                                                                  const T& val) 
                                             { ::new((void *)p) T( val); } */

这在Bjarne Stroustrup的"c++…"中有详细描述。第3版"。下面是基于此编写的示例:

作为一般规则,标准容器分开分配从初始化开始(您编写的任何容器也应该如此)。标准容器使用非常复杂的机制来允许自定义分配和初始化,但是在默认情况下,它可以归结为使用operator new/operator delete 函数分配内存;位置new来初始化它,并显式调用析构函数来销毁对象。换句话说,而不是序列:

p = new T[n];
//  ...
delete [] p;
它使用

:

p = operator new( n * sizeof( T ) );
for ( int i = 0; i != n; ++ i ) {
    new ( p + i ) T( otherValue );
}
//  ...
for ( int i = 0; i != n; ++ i ) {
    p->~T();
}
operator delete( p );

这是一个彻底的简化,以显示基本概念。在实践中,由于例外的原因,它将更加复杂)

考虑emplace_back():最有可能的是vector分配了一块新的未初始化内存,然后运行placement new来就地复制构造对象。