Код, который вы показали, безопасен только для типов POD (Plain Old Data), где представление объекта тривиально и, таким образом, присвоение неструктурированному объекту в порядке.
Если вы хотите, чтобы это работало ввсе общности (что я предполагаю, что вы делаете из-за использования шаблона), то для типа T
это неопределенное поведение, чтобы использовать объект до его построения.То есть вы должны создать объект до того, как, например, вы назначите это место.Это означает, что вам нужно явно вызывать конструктор по требованию.Следующий блок кода демонстрирует пример этого:
template <typename T, size_t NUM_ITEMS>
void Vector<T, NUM_ITEMS>::push_back(const T& val)
{
// potentially an overflow test here
// explicitly call copy constructor to create the new object in the buffer
new (reinterpret_cast<T*>(storage_) + size_) T(val);
// in case that throws, only inc the size after that succeeds
++size_;
}
Приведенный выше пример демонстрирует размещение new, которое принимает форму new (void*) T(args...)
.Он вызывает конструктор, но фактически не выполняет выделение.Визуальным отличием является включение аргумента void*
для самого оператора new, который является адресом объекта, на который нужно воздействовать и для которого вызывается конструктор.
И, конечно, при удалении элемента вы будетенужно уничтожить это явно.Чтобы сделать это для типа T
, просто вызовите псевдо-метод ~T()
для объекта.В контекстном контексте компилятор определит, что это означает, либо фактический вызов деструктора, либо отсутствие операций, например, для int или double.Это продемонстрировано ниже:
template<typename T, size_t NUM_ITEMS>
void Vector<T, NUM_ITEMS>::pop_back()
{
if (size_ > 0) // safety test, you might rather this throw, idk
{
// explicitly destroy the last item and dec count
// canonically, destructors should never throw (very bad)
reinterpret_cast<T*>(storage_)[--size_].~T();
}
}
Кроме того, я бы не стал возвращать ссылку на массив в вашем методе get_storage()
, поскольку он имеет информацию о длине и, по-видимому, подразумевает, что все элементы действительны (созданы)объекты, которые, конечно, они не.Я предлагаю вам предоставить методы для получения указателя на начало непрерывного массива построенных объектов и еще один метод для получения количества построенных объектов.Это методы .data()
и .size()
, например std::vector<T>
, которые сделают ваш класс менее раздражающим для опытных пользователей C ++.