reserve() 在 STL 中实现 std::vector

reserve() Implementation for std::vector in STL

本文关键字:实现 std vector STL reserve      更新时间:2023-10-16

考虑一下 std::vector::reserve() 的实现,来自"The C++ Programming Language, 4th ed., Bjarne Stroustrup"一书:

template<class T, class A>
void vector<T,A>::reserve(size_type newalloc)
{
    if (newalloc<=capacity()) return;
    vector_base<T,A> b {vb.alloc,newalloc};          // get new storage
                                                     // (see PS of question for details on vb data member)
    T* src  = elem;                                  // ptr to the start of old storage
    T* dest = b.elem;                                // ptr to the start of new storage       
    T* end  = elem+size();                           // past-the-end ptr to old storage
    for (; src!=end; ++src, ++dest) {
        new(static_cast<void*>(dest)) T{move(*src)}; // move construct
        src–>~T();                                   // destroy
    }
    swap(vb,b);                                      // install new base (see PS if needed)
} // implicitly release old space(when b goes out of scope)

请注意,在循环中,对于 vector 中的每个元素,至少对 ctor 和 dtor 进行了一次调用(如果元素的类具有基数,或者如果类或其基具有带有 ctors 的数据成员,则可能会触发更多此类调用)。(在书中,for-loop实际上是一个单独的函数,但为了简单起见,我在这里将其注入到reserve()中。

现在考虑我建议的替代方案:

template<class T, class A>
void vector<T,A>::reserve(size_type newalloc)
{
    if (newalloc<=capacity()) return;
    vector_base<T,A> b {vb.alloc,newalloc};    // get new space
    memcpy(b.elem, elem, sz);                  // copy raw memory
                                               // (no calls to ctors or dtors)
    swap(vb,b);                                // install new base
} // implicitly release old space(when b goes out of scope)

对我来说,最终结果似乎是一样的,减去对 ctors/dtor 的调用。

是否存在这种替代方案会失败的情况,如果是这样,缺陷在哪里?


附言我认为这不太相关,但以下是vector类和vector_base类的数据成员:

// used as a data member in std::vector
template<class T, class A = allocator<T> >
struct vector_base {                    // memory structure for vector
     A alloc;       // allocator
     T* elem;       // start of allocation
     T* space;      // end of element sequence, start of space allocated for possible expansion
     T* last;       // end of allocated space
     vector_base(const A& a, typename A::size_type n)
         : alloc{a}, elem{alloc.allocate(n)}, space{elem+n}, last{elem+n} { }
     ~vector_base() { alloc.deallocate(elem,last–elem); } // releases storage only, no calls 
                                                          // to dtors: vector's responsibility        
     //...
};
// std::vector
template<class T, class A = allocator<T> >
class vector {
     vector_base<T,A> vb;            // the data is here
     void destroy_elements();
public:
     //...
};

这可能会失败:

  • 只有当你有一个 POD 向量时,memcpy()才会起作用。

  • 对于所有其他类型的对象,它将失败,因为它不尊重它复制的对象的语义(复制构造)。

问题示例:

  • 如果对象的构造函数设置了一些指向内部成员的内部指针,则memcpy()将复制原始指针的值,该值将不会正确更新,并继续指向将要释放的内存区域。
  • 如果对象包含 shared_ptr ,则对象计数将变得不一致(memcpy()将复制指针而不增加其引用计数,则swap()将确保原始共享指针将在 b 中,这将被释放,以便共享指针引用计数将递减)。

正如 T.C 在评论中指出的那样,一旦您的向量存储了非 POD 数据,memcpy()就会产生 UB(未定义的行为)。