1) std::vector
A std::vector
действительно является правильным C ++ способом сделать это.
std::vector<Bullet> bullets;
bullets.reserve(10); // allocate memory for bullets without constructing any
bullets.push_back(Bullet(10.2,"Bang")); // put a Bullet in the vector.
bullets.emplace_back(10.2,"Bang"); // (C++11 only) construct a Bullet in the vector without copying.
2) new []
оператор
Также возможно сделать это с new
, но вам действительно не следует. Управление ресурсами с помощью new
/ delete
вручную - сложная задача, похожая на шаблонное метапрограммирование, поскольку ее лучше оставить разработчикам библиотек, которые будут использовать эти функции для создания эффективных библиотек высокого уровня для вас. Фактически, чтобы сделать это правильно, вы, в основном, будете реализовывать внутренние компоненты std::vector
.
Когда вы используете оператор new
для выделения массива, каждый элемент в массиве инициализируется по умолчанию. Ваш код может работать, если вы добавите конструктор по умолчанию в Bullet
:
class Bullet {
public:
Bullet() {} // default constructor
Bullet(double,std::string const &) {}
};
std::unique_ptr<Bullet[]> b = new Bullet[10]; // default construct 10 bullets
Затем, когда у вас есть реальные данные для Bullet
, вы можете назначить их одному из элементов массива:
b[3] = Bullet(20.3,"Bang");
Обратите внимание на использование unique_ptr
для обеспечения надлежащей очистки и исключительной безопасности. Делать эти вещи вручную сложно и подвержено ошибкам.
3) operator new
Оператор new
инициализирует свои объекты в дополнение к выделению для них места. Если вы хотите просто выделить место, вы можете использовать operator new
.
std::unique_ptr<Bullet,void(*)(Bullet*)> bullets(
static_cast<Bullet*>(::operator new(10 * sizeof(Bullet))),
[](Bullet *b){::operator delete(b);});
(Обратите внимание, что unique_ptr
гарантирует, что хранилище будет освобождено, но не более. В частности, если мы создаем какие-либо объекты в этом хранилище, мы должны вручную уничтожить их и сделать это безопасным способом.)
bullets
теперь указывает на объем памяти, достаточный для массива Bullet
с. Вы можете построить массив в этом хранилище:
new (bullets.get()) Bullet[10];
Однако конструкция массива снова использует инициализацию по умолчанию для каждого элемента, чего мы пытаемся избежать.
AFAIK C ++ не определяет какой-либо четко определенный метод построения массива без конструирования элементов. Я полагаю, что это во многом потому, что это было бы бесполезным для большинства (всех?) Реализаций C ++. Поэтому, хотя следующее технически не определено, на практике оно довольно четко определено.
bool constructed[10] = {}; // a place to mark which elements are constructed
// construct some elements of the array
for(int i=0;i<10;i+=2) {
try {
// pretend bullets points to the first element of a valid array. Otherwise 'bullets.get()+i' is undefined
new (bullets.get()+i) Bullet(10.2,"Bang");
constructed = true;
} catch(...) {}
}
Это будет создавать элементы массива без использования конструктора по умолчанию. Вам не нужно создавать каждый элемент, только те, которые вы хотите использовать. Однако при уничтожении элементов вы должны помнить, чтобы уничтожать только те элементы, которые были построены.
// destruct the elements of the array that we constructed before
for(int i=0;i<10;++i) {
if(constructed[i]) {
bullets[i].~Bullet();
}
}
// unique_ptr destructor will take care of deallocating the storage
Выше приведен довольно простой случай. Сделать нетривиальное использование исключения этого метода безопасным, не заключая его в класс, сложнее. Заключение этого в класс в основном равносильно реализации std::vector
.
4) std::vector
Так что просто используйте std::vector
.