У меня есть два метода создания списка объектов во время выполнения, у которых есть потенциальные недостатки, которые я хотел бы рассмотреть. В конечном счете мне интересно, какой еще e
Критерии моей проблемы:
Мое понимание:
Соответствует ли это рассуждение намерению разместить новое или мне не хватает более подходящего стандартного контейнера?
В приведенном ниже примере параметры списка объектов 1 и 2 представляют собой подход на основе std::vector и новое размещение соответственно. Для простоты я исключил любую обработку исключений. Идеальное использование в нисходящем направлении (например, без изменения размера) выглядит для меня одинаково (доступ через оператор []).
Для справки std::vector::emplace по сравнению с новым размещением может показаться уместным вопросом, но я пытаюсь найти идеальное решение для этого конкретного набора критериев, где другой вопрос значительно более расплывчатый. Также возможно, что другой контейнер или шаблон лучше решит эту проблему, и что два варианта здесь являются просто отправной точкой.
#include <vector>
class minorObj{
const int value;
public:
minorObj() = delete;
minorObj(int x):value(x){}
};
class majorObj{
const int N;
public:
std::vector<minorObj> objList_op1;
minorObj* objList_op2;
majorObj(int size):N(size){
objList_op1.reserve(N);
objList_op2 = static_cast<minorObj*>(operator new[](N * sizeof(minorObj)));
for(int i = 0; i<N;i++){
objList_op1.emplace_back(minorObj(i));
new(&objList_op2[i]) minorObj(i);
}
}
~majorObj(){
for(int i = N -1; i >= 0; i--){
objList_op2[i].~minorObj();
}
}
};
void someFn(){majorObj(25);} //added some different scope to emphasize need for proper memory mgmt
int main(){
someFn();
return 0;
}
Почему objList_op1
общедоступен в примере? Разве приватность не решит вашу проблему с изменением размера?
Если вам нужен массив фиксированного размера, просто используйте std::array
.
«Не желайте реализовывать конструктор по умолчанию для второстепенного объекта, поскольку он будет иметь некоторый параметр const, который назначается с помощью пользовательского ввода». Обычно это нежелательный подход, поскольку он приводит к осложнениям, подобным тому, который задается здесь. Вместо этого инварианты класса можно поддерживать с помощью инкапсуляции и сеттеров/геттеров.
Примечание: за 25 с лишним лет работы профессиональным разработчиком я никогда не сталкивался с ситуацией, когда размещение new было бы правильным решением проблемы — я бы очень скептически отнесся к этому пути.
@FrançoisAndrieux Я бы не согласился. Моделирование пустого/недопустимого состояния для объектов, которые логически не должны иметь такого состояния, усложняет инварианты.
@JesperJuhl OP явно передает размер конструктору во время выполнения.
@Quentin Объект не обязательно должен иметь значение NULL, чтобы извлечь выгоду из семантики значений. Если вы решите, что сконструированный по умолчанию экземпляр эквивалентен перемещенному экземпляру, имеет смысл сделать большинство типов конструируемыми по умолчанию. Этот вопрос является хорошим примером, точно так же, как после перемещения у вас есть объект шелухи, иногда вам нужно место для размещения значения до того, как значение будет готово для предоставления. Иногда объект не представляет никакого конкретного значения, независимо от того, находится ли то, что он представляет, в состоянии логической пустоты или нет.
@JesperJuhl не нужно знать размер во время компиляции для std::array? В противном случае я бы пошел с этим.
@Nelfeal В реальном коде он является частным, но это не имеет значения, поскольку я не хочу, чтобы majorObj менял размер своего собственного массива.
@20lbpizza Разве ты не пишешь majorObj
? Зачем вам делать то, чего вы не хотите? Моргни дважды, если кто-то заставляет тебя.
Возможно, вы хотите что-то вроде fixed_capacity_vector (или static_vector boost? Непонятно, чего именно вы хотите достичь.
@Quentin Да, класс-оболочка, вероятно, добьется цели и практически не потребует накладных расходов, если я не ошибаюсь? Я мог бы просто раскрыть функциональность std::vector, которую мы хотим поддерживать, и на этом закончить. Гораздо проще, чем иметь дело с этим распределением в стиле C (которое, как вы уже указали, не является полным в этом примере)!
@Nelfeal fixed_capacity_vector выглядит как потенциальная альтернатива, но похоже, что вы можете удалить. По сути, мне нужен вектор с очень ограниченной функциональностью.
Основываясь на комментарии Квентина выше, пример реализации класса-оболочки можно выполнить с помощью следующего класса шаблона. Это позволяет использовать преимущества инициализации во время выполнения std::vector, разрешая/запрещая другие операции по мере необходимости. Доступ к содержащимся в нем объектам будет таким же через оператор нижнего индекса.
template <class T>
class RuntimeArray{
private:
std::vector<T> values;
public:
RuntimeArray() = delete;
RuntimeArray(const std::vector<int>& inputs){ // can be whatever type to initialize
values.reserve(inputs.size());
for(int i = 0; i < inputs.size(); i++){
values.emplace_back(inputs[i]);
}
}
T& operator [](int i){
return values[i];
}
const T& operator[](int i) const{
return values[i];
}
// std::vector without std::vector::push_back or std::vector::erase etc.
};
Пара замечаний: конструктор должен использовать inputs
как константную ссылку, чтобы избежать копирования. emplace_back
должен взять inputs[i]
напрямую, чтобы создать T
на месте. Также должна быть const
версия operator[]
.
Как насчет того, чтобы обернуть
std::vector
в класс, который предоставляет только функции, которые не могут изменить свой размер? Примечание: этотmajorObj
сгорит, как только будет скопирован.