std::vector
debe inicializar los valores de la matriz de alguna manera, lo que significa algunos constructor (o constructor de copia) deben ser llamados. El comportamiento de vector
(o cualquier clase de contenedor) no está definido si tuviera acceso a la sección no inicializada de la matriz como si se hubiera inicializado.
La mejor manera es usar reserve()
y push_back()
, para que se use el constructor de copias, evitando la construcción predeterminada.
Usando el código de ejemplo:
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]));
}
}
El único problema con llamar reserve()
(o resize()
) como esto es que usted puede terminar invocando el constructor de copia más a menudo de lo necesario. Si puede hacer una buena predicción sobre el tamaño final de la matriz, es mejor que reserve()
el espacio una vez al principio. Sin embargo, si no conoce el tamaño final, al menos la cantidad de copias será mínima en promedio.
En la versión actual de C++, el bucle interno es un poco ineficiente ya que se construye un valor temporal en la pila, se construye una copia a la memoria de los vectores y finalmente se destruye el temporal. Sin embargo, la próxima versión de C++ tiene una característica llamada referencias R-Value (T&&
) que ayudará.
La interfaz suministrada por std::vector
no permite otra opción, que es usar alguna clase de fábrica para construir valores distintos al predeterminado. Aquí es un ejemplo aproximada de lo que este patrón se vería implementado en 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));
}
}
Hacer esto no significa que tenga que crear su propia clase de vectores. En este caso, también complica lo que debería haber sido un simple ejemplo. Pero puede haber ocasiones en las que usar una función de fábrica como esta sea mejor, por ejemplo, si el inserto es condicional en algún otro valor, y de otro modo tendrías que construir incondicionalmente algún costoso temporal incluso si no fuera realmente necesario.
Nota - con ayuda de reserva() no es una solución, ya que no puede acceder legalmente los datos que están en ubicaciones finales() y por encima. –
Otra aclaración: no es que el constructor inicialice los valores en 0. Es que el tamaño de las llamadas se inserta, lo que hace. –
¿Podría darnos la declaración de estructura también? Gracias ... :-) – paercebal