Hola, hoy hice una pregunta sobre cómo insertar diferentes tipos de objetos en la misma matriz de vectores y mi código en esa pregunta fue
gate* G[1000];
G[0] = new ANDgate() ;
G[1] = new ORgate;
//gate is a class inherited by ANDgate and ORgate classes
class gate
{
.....
......
virtual void Run()
{ //A virtual function
}
};
class ANDgate :public gate
{.....
.......
void Run()
{
//AND version of Run
}
};
class ORgate :public gate
{.....
.......
void Run()
{
//OR version of Run
}
};
//Running the simulator using overloading concept
for(...;...;..)
{
G[i]->Run() ; //will run perfectly the right Run for the right Gate type
}
y quería usar vectores, así que alguien escribió que debería hacer eso:
std::vector<gate*> G;
G.push_back(new ANDgate);
G.push_back(new ORgate);
for(unsigned i=0;i<G.size();++i)
{
G[i]->Run();
}
pero luego él y muchos otros sugirieron que sería mejor usar los contenedores de puntero Boost
o shared_ptr
. He pasado las últimas 3 horas leyendo sobre este tema, pero la documentación me parece bastante avanzada. **** ¿Alguien puede darme un pequeño ejemplo de código de shared_ptr
uso y por qué sugirieron usar shared_ptr
. También hay otros tipos como ptr_vector
, ptr_list
y ptr_deque
** **
Edit1: también he leído un ejemplo de código que incluía:
typedef boost::shared_ptr<Foo> FooPtr;
.......
int main()
{
std::vector<FooPtr> foo_vector;
........
FooPtr foo_ptr( new Foo( 2 ) );
foo_vector.push_back( foo_ptr );
...........
}
¡Y no entiendo la sintaxis!
c++
boost
vector
shared-ptr
smart-pointers
Ahmed
fuente
fuente
main
crea un vector que puede contener punteros compartidos a un tipo llamadoFoo
; el segundo crea unFoo
usonew
y un puntero compartido para administrarlo; el tercero coloca una copia del puntero compartido en el vector.Respuestas:
El uso
vector
deshared_ptr
elimina la posibilidad de perder memoria porque se olvidó de recorrer el vector y llamardelete
a cada elemento. Repasemos una versión ligeramente modificada del ejemplo línea por línea.typedef boost::shared_ptr<gate> gate_ptr;
Cree un alias para el tipo de puntero compartido. Esto evita la fealdad en el lenguaje C ++ que resulta de escribir
std::vector<boost::shared_ptr<gate> >
y olvidar el espacio entre los signos de cierre mayor que .std::vector<gate_ptr> vec;
Crea un vector vacío de
boost::shared_ptr<gate>
objetos.gate_ptr ptr(new ANDgate);
Asigne una nueva
ANDgate
instancia y guárdela en un archivoshared_ptr
. La razón para hacer esto por separado es evitar un problema que puede ocurrir si se produce una operación. Esto no es posible en este ejemplo. Las "Mejores prácticas" de Boostshared_ptr
explican por qué es una mejor práctica realizar la asignación en un objeto independiente en lugar de en uno temporal.vec.push_back(ptr);
Esto crea un nuevo puntero compartido en el vector y lo copia
ptr
. El recuento de referencias en las entrañas deshared_ptr
asegura que el objeto asignado dentro deptr
se transfiera de forma segura al vector.Lo que no se explica es que el destructor
shared_ptr<gate>
asegura que se borre la memoria asignada. Aquí es donde se evita la pérdida de memoria. El destructor forstd::vector<T>
asegura queT
se llama al destructor for para cada elemento almacenado en el vector. Sin embargo, el destructor de un puntero (por ejemplo,gate*
) no elimina la memoria que había asignado . Eso es lo que está intentando evitar utilizandoshared_ptr
optr_vector
.fuente
int x(5);
para inicializarx
con el valor 5. En este caso, se inicializa con el valor de la nueva expresión que crea unANDgate
; el valor de la nueva expresión es un puntero al nuevo objeto.Voy a añadir que una de las cosas importantes acerca de
shared_ptr
's es única cada vez que construir con la siguiente sintaxis:shared_ptr<Type>(new Type(...));
De esta manera, el puntero "real" a
Type
es anónimo para su alcance y solo lo mantiene el puntero compartido. Por lo tanto, será imposible que utilice accidentalmente este puntero "real". En otras palabras, nunca hagas esto:Type* t_ptr = new Type(...); shared_ptr<Type> t_sptr ptrT(t_ptr); //t_ptr is still hanging around! Don't use it!
Aunque esto funcionará, ahora tiene un
Type*
puntero (t_ptr
) en su función que vive fuera del puntero compartido. Es peligroso usarlo ent_ptr
cualquier lugar, porque nunca se sabe cuándo el puntero compartido que lo contiene puede destruirlo, y se producirá un error de segmentación.Lo mismo ocurre con los punteros que le devuelven otras clases. Si una clase que no escribiste te entrega un puntero, generalmente no es seguro ponerlo en un
shared_ptr
. No, a menos que esté seguro de que la clase ya no usa ese objeto. Porque si lo coloca en ashared_ptr
, y queda fuera del alcance, el objeto se liberará cuando la clase aún lo necesite.fuente
auto t_ptr = make_shared<Type>(...);
o de manera equivalenteshared_ptr<Type> t_ptr = make_shared<Type>(...);
, simplemente porque esa forma es más eficiente.,
entret_sptr
yptrT
enshared_ptr<Type> t_sptr ptrT(t_ptr);
?Aprender a usar punteros inteligentes es, en mi opinión, uno de los pasos más importantes para convertirse en un programador competente en C ++. Como sabe, cada vez que crea un objeto nuevo, en algún momento desea eliminarlo.
Un problema que surge es que, con excepciones, puede ser muy difícil asegurarse de que un objeto siempre se libere una sola vez en todas las rutas de ejecución posibles.
Esta es la razón de RAII: http://en.wikipedia.org/wiki/RAII
Hacer una clase auxiliar con el propósito de asegurarse de que un objeto siempre se elimine una vez en todas las rutas de ejecución.
Ejemplo de una clase como esta es: std :: auto_ptr
Pero a veces te gusta compartir objetos con otros. Solo debe eliminarse cuando nadie lo use más.
Para ayudar con eso, se han desarrollado estrategias de recuento de referencias, pero aún necesita recordar addref y liberar ref manualmente. En esencia, este es el mismo problema que nuevo / eliminar.
Es por eso que boost ha desarrollado boost :: shared_ptr, su puntero inteligente de conteo de referencias para que pueda compartir objetos y no perder memoria involuntariamente.
Con la adición de C ++ tr1, esto ahora también se agrega al estándar c ++, pero se llama std :: tr1 :: shared_ptr <>.
Recomiendo usar el puntero compartido estándar si es posible. ptr_list, ptr_dequeue y también los contenedores especializados de IIRC para tipos de puntero. Los ignoro por ahora.
Entonces, podemos comenzar con su ejemplo:
std::vector<gate*> G; G.push_back(new ANDgate); G.push_back(new ORgate); for(unsigned i=0;i<G.size();++i) { G[i]->Run(); }
El problema aquí es ahora que cada vez que G sale del alcance, filtramos los 2 objetos agregados a G. Vamos a reescribirlo para usar std :: tr1 :: shared_ptr
// Remember to include <memory> for shared_ptr // First do an alias for std::tr1::shared_ptr<gate> so we don't have to // type that in every place. Call it gate_ptr. This is what typedef does. typedef std::tr1::shared_ptr<gate> gate_ptr; // gate_ptr is now our "smart" pointer. So let's make a vector out of it. std::vector<gate_ptr> G; // these smart_ptrs can't be implicitly created from gate* we have to be explicit about it // gate_ptr (new ANDgate), it's a good thing: G.push_back(gate_ptr (new ANDgate)); G.push_back(gate_ptr (new ORgate)); for(unsigned i=0;i<G.size();++i) { G[i]->Run(); }
Cuando G sale del alcance, la memoria se recupera automáticamente.
Como ejercicio con el que asedié a los recién llegados a mi equipo es pedirles que escriban su propia clase de puntero inteligente. Luego, una vez que haya terminado, descarte la clase inmediatamente y nunca la vuelva a usar. Es de esperar que haya adquirido un conocimiento crucial sobre cómo funciona un puntero inteligente bajo el capó. Realmente no hay magia.
fuente
for( auto itt = G.begin(); itt != G.end(); ++itt ){ itt->Run(); }
La documentación de boost proporciona un ejemplo de inicio bastante bueno: ejemplo shared_ptr (en realidad se trata de un vector de punteros inteligentes) o shared_ptr doc La siguiente respuesta de Johannes Schaub explica bastante bien los punteros inteligentes de boost: explicación de los punteros inteligentes
La idea detrás (en la menor cantidad de palabras posible) ptr_vector es que maneja la desasignación de la memoria detrás de los punteros almacenados por usted: digamos que tiene un vector de punteros como en su ejemplo. Al salir de la aplicación o abandonar el ámbito en el que se define el vector, tendrá que limpiar después de usted mismo (ha asignado dinámicamente ANDgate y ORgate) pero simplemente borrar el vector no lo hará porque el vector está almacenando los punteros y no los objetos reales (no destruirá sino lo que contiene).
// if you just do G.clear() // will clear the vector but you'll be left with 2 memory leaks ... // to properly clean the vector and the objects behind it for (std::vector<gate*>::iterator it = G.begin(); it != G.end(); it++) { delete (*it); }
boost :: ptr_vector <> se encargará de lo anterior por usted, lo que significa que desasignará la memoria detrás de los punteros que almacena.
fuente
A través de Boost puedes hacerlo>
std::vector<boost::any> vecobj; boost::shared_ptr<string> sharedString1(new string("abcdxyz!")); boost::shared_ptr<int> sharedint1(new int(10)); vecobj.push_back(sharedString1); vecobj.push_back(sharedint1);
> para insertar un tipo de objeto diferente en su contenedor de vectores. mientras que para acceder tienes que usar any_cast, que funciona como dynamic_cast, espera que funcione para tus necesidades.
fuente
#include <memory> #include <iostream> class SharedMemory { public: SharedMemory(int* x):_capture(x){} int* get() { return (_capture.get()); } protected: std::shared_ptr<int> _capture; }; int main(int , char**){ SharedMemory *_obj1= new SharedMemory(new int(10)); SharedMemory *_obj2 = new SharedMemory(*_obj1); std::cout << " _obj1: " << *_obj1->get() << " _obj2: " << *_obj2->get() << std::endl; delete _obj2; std::cout << " _obj1: " << *_obj1->get() << std::endl; delete _obj1; std::cout << " done " << std::endl; }
Este es un ejemplo de shared_ptr en acción. _obj2 se eliminó pero el puntero sigue siendo válido. la salida es, ./test _obj1: 10 _obj2: 10 _obj2: 10 hecho
fuente
La mejor manera de agregar diferentes objetos en el mismo contenedor es usar make_shared, vector y bucle basado en rango y tendrás un código agradable, limpio y "legible".
typedef std::shared_ptr<gate> Ptr vector<Ptr> myConatiner; auto andGate = std::make_shared<ANDgate>(); myConatiner.push_back(andGate ); auto orGate= std::make_shared<ORgate>(); myConatiner.push_back(orGate); for (auto& element : myConatiner) element->run();
fuente