c++ - STL 中 std::vector 的 reserve() 实现

标签 c++ c++11 vector stl

考虑“The C++ Programming Language, 4th ed., Bjarne Stroustrup”一书中 std::vector::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 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 进行一次调用(如果元素的类具有基类,或者类或其基类具有数据成员,则可能触发更多此类调用与 Actor )。 (在书中,for-loop实际上是一个单独的函数,但为了简单起见,我在这里将它注入(inject)到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/dtors 的调用。

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


附言我认为这不是很相关,但这里是 vectorvector_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:
     //...
};

最佳答案

这可能会失败:

  • memcpy() 仅当您有 POD vector 时才有效。

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

问题示例:

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

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

关于c++ - STL 中 std::vector 的 reserve() 实现,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34821661/

相关文章:

c++ - 如何从 vector<string> 中提取第一个元素并在循环中将 vector 大小减少 1

c++ - 不能在从 vector 派生的类中交换结构

c++ - 限制 Boost Log v2 中轮换日志的大小

c++ - 同时对两个 vector (键/值)进行排序的最快方法?

c++ - 使用 constexpr 或模板元编程简化较长的展开循环表达式

c++ - C 数组的大小作为函数?

C++ 结构体 vector ,为什么我不能使用 vector .at(i) 或 vector [i] 作为结构体?

c++ - 在C++上的gRPC中跟踪支持

C++ 将对象 vector 中的元素复制到具有此元素的 vector 中

c++ - MongoDB C++ 查询文档的子元素