std::vector
обычно реализуется путем выделения памяти через operator new
или некоторого распределителя, передаваемого в векторный класс, и создания новых объектов в этом хранилище. Вот некоторая демонстрация для случая вызова operator new
напрямую без использования распределителя. (Реальное std::vector
всегда использует распределитель, std::allocator
по умолчанию.):
_vector_data = static_cast<T*>(::operator new(sizeof(T)*new_capacity));
Это выделяет память, но явно не создает никаких объектов. (Обратите внимание, что это память правильно выровнена только для типов с невыровненным выравниванием , но это должно быть приемлемым ограничением.)
Затем, чтобы поместить новый объект в это хранилище, вы должны использовать нераспределенное место размещения новое :
template <typename T>
void Vector<T>::push_back(T&& c)
{
::new(static_cast<void*>(_vector_data+_vector_size)) T(std::move(c));
++_vector_size;
}
(только для того, чтобы сначала убедиться, что емкости достаточно)
Если вас это не волнует В некоторых случаях, связанных с перегрузками класса operator new
, вы можете использовать
new(_vector_data+_vector_size) T(std::move(c));
вместо
::new(static_cast<void*>(_vector_data+_vector_size)) T(std::move(c));
и
operator new
вместо
::operator new
Чтобы удалить элементы, вы бы затем явно вызвали их деструкторы, например,
(_vector_data+_vector_size)->~T();
и освободили бы память, вызвав operator delete
, соответствующий форме, которую вы использовали для operator new
.
Технически это имеет неопределенное поведение до C ++ 20, потому что арифметика указателя c, как в _vector_data+_vector_size
, допустима только в том случае, если _vector_data
указывает на элемент массива, но мы никогда не создавали массив. Начиная с C ++ 20 этот массив создается неявно .
Невозможно реализовать std::vector
, который имеет правильную семантику массива для указателей на его элементы в соответствии с заданными вами требованиями. до C ++ 20, не полагаясь на семантику, которая является неопределенным поведением в соответствии со стандартом, хотя это неопределенное поведение является скорее техническим, и, вероятно, никогда не вызовет проблем на практике.