reserve () реализация для std:: vector в STL



Рассмотрим эту реализацию std:: vector:: reserve () из книги " Язык программирования C++, 4-е изд., Бьярне Страуструп:



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)


Обратите внимание, что в цикле для каждого элемента в векторе выполняется по крайней мере один вызов ctor и dtor(возможно, вызывается больше таких вызовов, если класс элемента имеет базы, или если класс или его базы имеют элементы данных с ctors). (В книге for-loop фактически является отдельной функцией, но я ввел ее в резерв () здесь для простота.)

Теперь рассмотрим предложенную мной альтернативу:



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.



Существует ли ситуация, в которой эта альтернатива потерпела бы неудачу, и если да, то где недостаток?



P.S. Я не думаю, что это имеет большое значение, но вот данные-члены классов 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:
//...
};
622   1  

1 ответ:

Это может не сработать:

  • memcpy() будет работать только в том случае, если у вас есть вектор POD.

  • Он не будет работать для всех других типов объектов, поскольку он не уважает семантику объектов, которые он копирует (конструкция копирования).

Пример вопросов:

  • Если конструктор объекта устанавливает некоторые внутренние указатели на внутренние члены, ваш memcpy() скопирует значение исходного указателя, которое не будет обновляться корректно и продолжит укажите на область памяти, которая будет освобождена.
  • Если объект содержит a shared_ptr, количество объектов станет несогласованным (memcpy() будет дублировать указатель без увеличения его количества ссылок, тогда swap() удостоверится, что исходный общий указатель будет находиться в b, который будет освобожден, так что общее количество ссылок указателя будет уменьшено).

Как было указано T. C в комментариях, как только ваш вектор хранит данные не POD, memcpy() приводит к UB (неопределенное поведение).

Comments

    Ничего не найдено.