如何实现std :: vector insert? C ++

时间:2014-06-26 08:02:31

标签: c++ vector stl copy-constructor assignment-operator

最近我重读了ISO C ++标准,并发现了非常有趣的注意事项:

  

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

但等等?

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

  1. 如何完成此操作,因为类型T可能没有默认构造函数?
  2. 然后分配,在分配内存后,我们必须将旧值分配给新内存,对吧?
  3. 考虑到这两件事,std::vector如何通过“仅复制构造函数”来做到这一点?使用了哪些实现和语言习语?

3 个答案:

答案 0 :(得分:27)

通过调用 allocator 函数 allocate()来获取原始内存并调用allocator construct (iterator,val) )使用 placement 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版”中详细介绍了这一点。 Here是基于此编写的示例。

答案 1 :(得分:4)

作为一般规则,标准容器分开分配 从初始化开始(就像你写的任何容器一样)。 标准容器使用非常复杂的机制 允许自定义分配和初始化,但是 在默认情况下,它归结为使用 operator new / operator delete 函数分配内存, placement new用于初始化它,以及显式调用 析构函数来销毁对象。换句话说,insteaad 顺序:

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 );

(这是一个彻底的简化,以显示基本概念。 在实践中,出于例外的原因,它会更复杂 例如,安全。)

答案 2 :(得分:1)

考虑一下emplace_back():很可能vector会分配一块新的unititialized内存,然后运行placement new来就地复制构造对象。