una - C++ vector de objetos vs. vector de punteros a objetos
vector de vectores c++ (6)
Mi pregunta es simple: ¿por qué funcionaba un vector de punteros y cuándo se crearía un vector de objetos frente a un vector de punteros a esos objetos?
std :: vector es como una matriz sin formato asignada con nueva y reasignada cuando intenta insertar más elementos que su tamaño actual.
Entonces, si contiene punteros A, es como si estuvieras manipulando una matriz de A *. Cuando necesite cambiar el tamaño (usted empuja hacia atrás un elemento mientras que ya está llena la capacidad actual), creará otro conjunto A * y copiará este desde el anterior.
Si contiene objetos A, entonces es como si estuvieras manipulando una matriz de A, por lo que A debería ser predecible si se producen realocations automáticas. En ese caso, todos los objetos A se copian también en otra matriz.
¿Ver la diferencia? Los objetos A en std :: vector pueden cambiar de dirección si realiza algunas manipulaciones que requieren cambiar el tamaño de la matriz interna. De ahí viene la mayoría de los problemas con la contención de objetos en std :: vector.
Una forma de usar std :: vector sin tener tales problemas es asignar una matriz lo suficientemente grande desde el principio. La palabra clave aquí es "capacidad". La capacidad std :: vectorial es el tamaño real del búfer de memoria en el que colocará los objetos. Entonces, para configurar la capacidad, tienes dos opciones:
1) dimensione su std :: vector en la construcción para construir todo el objeto desde el principio, con la cantidad máxima de objetos, que llamará a los constructores de cada objeto.
2) una vez que std :: vector está construido (pero no tiene nada), use su función reserve () : asignará un búfer lo suficientemente grande (usted proporciona el tamaño máximo del vector). IT establecerá la capacidad. Si empuja hacia atrás objetos en este vector o cambia el tamaño () por debajo del límite del tamaño que ha proporcionado en la llamada a reserva (), nunca reasignará el búfer interno y sus objetos no cambiarán la ubicación en la memoria, haciendo punteros a esos objetos siempre válido (algunas afirmaciones para verificar que el cambio de capacidad nunca ocurra es una práctica excelente).
Estoy escribiendo una aplicación usando openFrameworks, pero mi pregunta no es específica solo de oF; más bien, es una pregunta general sobre los vectores C ++ en general.
Quería crear una clase que contiene múltiples instancias de otra clase, pero también proporciona una interfaz intuitiva para interactuar con esos objetos. Internamente, mi clase usó un vector de la clase, pero cuando traté de manipular un objeto usando vector.at (), el programa se compilaría pero no funcionaría correctamente (en mi caso, no mostraría un video).
// instantiate object dynamically, do something, then append to vector
vector<ofVideoPlayer> videos;
ofVideoPlayer *video = new ofVideoPlayer;
video->loadMovie(filename);
videos.push_back(*video);
// access object in vector and do something; compiles but does not work properly
// without going into specific openFrameworks details, the problem was that the video would
// not draw to screen
videos.at(0)->draw();
En algún lugar, se sugirió que haga un vector de punteros a objetos de esa clase en lugar de un vector de esos objetos. Implementé esto y de hecho funcionó como un encanto.
vector<ofVideoPlayer*> videos;
ofVideoPlayer * video = new ofVideoPlayer;
video->loadMovie(filename);
videos.push_back(video);
// now dereference pointer to object and call draw
videos.at(0)->draw();
Estaba asignando memoria para los objetos dinámicamente, es decir, de ofVideoPlayer = new ofVideoPlayer;
Mi pregunta es simple: ¿por qué funcionaba un vector de punteros y cuándo se crearía un vector de objetos frente a un vector de punteros a esos objetos?
La idea principal de usar vectores es almacenar objetos en un espacio continuo, cuando se usa un puntero o un puntero inteligente que no sucederá
Lo que debes saber sobre los vectores en c ++ es que tienen que usar el operador de copia de la clase de tus objetos para poder ingresarlos en el vector. Si tenía asignación de memoria en estos objetos que se desasoció automáticamente cuando se llamó al destructor, eso podría explicar sus problemas: su objeto se copió en el vector y luego se destruyó.
Si tiene, en su clase de objeto, un puntero que apunta hacia un búfer asignado, una copia de este objeto apuntará hacia el mismo búfer (si usa el operador de copia predeterminado). Si el destructor desasigna el búfer, cuando se invoque el destructor de copia, se desasignará el búfer original, por lo tanto, sus datos ya no estarán disponibles.
Este problema no ocurre si usa punteros, porque controla la vida de sus elementos mediante new / destroy, y las funciones vectoriales solo copian el puntero hacia sus elementos.
Si está asignando memoria para los objetos usando new
, la está asignando en el montón. En este caso, debe usar punteros. Sin embargo, en C ++, la convención generalmente es crear todos los objetos en la pila y pasar copias de esos objetos en lugar de pasar punteros a objetos en el montón.
¿Por qué es esto mejor? Es porque C ++ no tiene recolección de basura, por lo que la memoria de los objetos en el montón no se recuperará a menos que delete
el objeto específicamente. Sin embargo, los objetos en la pila siempre se destruyen cuando salen del alcance. Si crea objetos en la pila en lugar del montón, minimiza el riesgo de pérdidas de memoria.
Si usa la pila en lugar del montón, tendrá que escribir buenos constructores de copia y destructores. Los constructores o destructores de copia mal escritos pueden generar fugas de memoria o duplicados.
Si sus objetos son demasiado grandes para ser copiados eficientemente, entonces es aceptable usar punteros. Sin embargo, debe usar punteros inteligentes de recuento de referencias (ya sea C ++ 0x auto_ptr o uno de Boost library punteros) para evitar pérdidas de memoria.
Usualmente no almaceno clases directamente en std::vector
. La razón es simple: no sabrías si la clase se deriva o no.
P.ej:
En encabezados:
class base
{
public:
virtual base * clone() { new base(*this); };
virtual ~base(){};
};
class derived : public base
{
public:
virtual base * clone() { new derived(*this); };
};
void some_code(void);
void work_on_some_class( base &_arg );
En fuente:
void some_code(void)
{
...
derived instance;
work_on_some_class(derived instance);
...
}
void work_on_some_class( base &_arg )
{
vector<base> store;
...
store.push_back(*_arg.clone());
// Issue!
// get derived * from clone -> the size of the object would greater than size of base
}
Entonces prefiero usar shared_ptr
:
void work_on_some_class( base &_arg )
{
vector<shared_ptr<base> > store;
...
store.push_back(_arg.clone());
// no issue :)
}
vector
adición de vector
y el mantenimiento interno utilizan copias del objeto original; si tomar una copia es muy costoso o imposible, es preferible utilizar un puntero.
Si convierte el vector
un puntero, use un puntero inteligente para simplificar su código y minimizar el riesgo de fugas.
¿Tal vez su clase no hace la construcción / asignación correcta (es decir, profunda)? De ser así, los punteros funcionarían pero no las instancias de objeto como el vector miembro.