std::vector
должен каким-то образом инициализировать значения в массиве, что означает, что должен быть вызван некоторый конструктор (или конструктор копирования). Поведение vector
(или любого класса контейнера) не определено, если бы вы обращались к неинициализированному разделу массива, как если бы он был инициализирован.
Лучший способ - использовать reserve()
и push_back()
, чтобы использовать конструктор копирования, избегая построения по умолчанию.
Используя ваш пример кода:
struct YourData {
int d1;
int d2;
YourData(int v1, int v2) : d1(v1), d2(v2) {}
};
std::vector<YourData> memberVector;
void GetsCalledALot(int* data1, int* data2, int count) {
int mvSize = memberVector.size();
// Does not initialize the extra elements
memberVector.reserve(mvSize + count);
// Note: consider using std::generate_n or std::copy instead of this loop.
for (int i = 0; i < count; ++i) {
// Copy construct using a temporary.
memberVector.push_back(YourData(data1[i], data2[i]));
}
}
Единственная проблема с таким вызовом reserve()
(или resize()
) состоит в том, что вы можете в конечном итоге вызывать конструктор копирования чаще, чем вам нужно. Если вы можете сделать хороший прогноз относительно окончательного размера массива, лучше reserve()
пробел один раз в начале. Если вы не знаете окончательный размер, по крайней мере, количество копий будет в среднем минимальным.
В текущей версии C ++ внутренний цикл немного неэффективен, так как временное значение создается в стеке, копируется в память векторов и, наконец, временное уничтожается. Однако в следующей версии C ++ есть функция, называемая ссылками R-Value (T&&
), которая поможет.
Интерфейс, предоставляемый std::vector
, не позволяет использовать другую опцию, которая заключается в использовании некоторого фабричного класса для создания значений, отличных от значений по умолчанию. Вот грубый пример того, как этот шаблон будет выглядеть в C ++:
template <typename T>
class my_vector_replacement {
// ...
template <typename F>
my_vector::push_back_using_factory(F factory) {
// ... check size of array, and resize if needed.
// Copy construct using placement new,
new(arrayData+end) T(factory())
end += sizeof(T);
}
char* arrayData;
size_t end; // Of initialized data in arrayData
};
// One of many possible implementations
struct MyFactory {
MyFactory(int* p1, int* p2) : d1(p1), d2(p2) {}
YourData operator()() const {
return YourData(*d1,*d2);
}
int* d1;
int* d2;
};
void GetsCalledALot(int* data1, int* data2, int count) {
// ... Still will need the same call to a reserve() type function.
// Note: consider using std::generate_n or std::copy instead of this loop.
for (int i = 0; i < count; ++i) {
// Copy construct using a factory
memberVector.push_back_using_factory(MyFactory(data1+i, data2+i));
}
}
Это означает, что вам нужно создать свой собственный векторный класс. В этом случае это также усложняет то, что должно было быть простым примером. Но могут быть случаи, когда использование фабричной функции, подобной этой, лучше, например, если вставка зависит от какого-то другого значения, и в противном случае вам придется безоговорочно создать некоторое дорогое временное значение, даже если оно на самом деле не нужно.