Foros del Web » Programación para mayores de 30 ;) » C/C++ »

Vector de punteros cuando muda a otro lado

Estas en el tema de Vector de punteros cuando muda a otro lado en el foro de C/C++ en Foros del Web. Hola amigos, estoy con el siguiente dilema, estoy trabajando sobre un grafo con nodos conectados entre sí. Y creé una clase llamada así, "SuperNodos": @import ...
  #1 (permalink)  
Antiguo 24/09/2016, 14:59
 
Fecha de Ingreso: junio-2014
Mensajes: 144
Antigüedad: 9 años, 10 meses
Puntos: 1
Vector de punteros cuando muda a otro lado

Hola amigos, estoy con el siguiente dilema, estoy trabajando sobre un grafo con nodos conectados entre sí. Y creé una clase llamada así, "SuperNodos":

Código C++:
Ver original
  1. class SuperNode{
  2. private:
  3.     int _id;
  4.     int _alias;
  5.     int _level;
  6.     int _pos;
  7.     vector<vector<SuperNode*>> _roadtoit;
  8.     vector<SuperNode*> _Neighbor;
  9. public:
  10.     SuperNode(){ ; }
  11.     SuperNode(int id, int alias, int level, int pos){ _id = id, _alias = alias, _level = level, _pos = pos; }
  12.     void add_Neighbor(SuperNode &n){ _Neighbor.push_back(&n); }
  13.     void add_roadtoit(vector<SuperNode*> &n){ _roadtoit.push_back(n); }
  14.     int get_id(){ return _id; }
  15.     int get_alias(){ return _alias; }
  16.     int get_level(){ return _level; }
  17.     int get_pos(){ return _pos; }
  18.     void set_pos(int i){ _pos = i; }
  19.     SuperNode* get_oneinroad(int row, int col){ return _roadtoit[row][col]; }
  20.     void set_oneinroad(int row, SuperNode &i){ _roadtoit[row].push_back(&i); }
  21.     vector<SuperNode*> get_roadtoit(int i){ return _roadtoit[i]; }
  22.     SuperNode get_neighbor(int i){ return *_Neighbor[i]; }
  23. };

Como ven tiene algunos métodos y sus atributos el importante para la pregunta es el
Código C++:
Ver original
  1. vector<vector<SuperNode*>> _roadtoit;
. Que es un vector de punteros que apuntan a unos SuperNodos.

En el main inicializo un vector de Nodos, llamado "s";

Código C++:
Ver original
  1. int main(){
  2.  
  3. n = lectraDedatos(); // lee los datos y lee cuantos nodos hay n
  4. vector<SuperNode> s (n); // se crea con un vector con n nodos
  5.  
  6. //aquí va un for muy largo.
  7.  
  8. return 0;
  9. }

La cuestión es que cada nodo de s tiene su información y eso, en el _roadtoit él guarda todos los posibles caminos para llegar a ese nodo, con punteros a nodos del vector s.

Dentro del for muy largo, luego de recorre el grafo, adiciono nodos ficticios a s con un pushback (quedando más de n claro). Pero me dí cuenta que como el vector estaba a su maxima capacidad, <vector> muda a otro sector de memoria donde si alcanza y le da una capacidad mayor al size, eso está bien, pero ahora lodos los punteros (cada componente de _roadtoit) que apuntaban a s, quedan apuntando a un lugar de memoria donde ya no hay nada. Cómo puedo evitar eso o bien actualizar a dichos punteros?

Muchas gracias,

--------------ejemplo claro---------------

Código C++:
Ver original
  1. #include <iostream>;
  2. #include <vector>;
  3.  
  4.  
  5. using namespace std;
  6.  
  7. int main() {
  8.     int i = 0;
  9.     vector<int> v(10,50);
  10.     int *p = &v[0];
  11.     cout << "Dirección del puntero:  " << p << endl;
  12.     cout << "contenido del puntero:  " << *p << endl << endl;
  13.  
  14.     v.push_back(100);
  15.  
  16.     cout << "Dirección del puntero:  " << p << endl;
  17.     cout << "contenido del puntero:  " << *p << endl << endl;
  18.    
  19.     int *p2 = &v[0];
  20.     cout << "Dirección del puntero 2:  " << p2 << endl;
  21.     cout << "contenido del puntero 2:  " << *p2 << endl << endl;
  22.  
  23.     cin.get();
  24.     return 0;
  25. }

El puntero p se pierde una vez se muda vector.

Última edición por dmorill; 24/09/2016 a las 15:15 Razón: más información
  #2 (permalink)  
Antiguo 26/09/2016, 01:24
 
Fecha de Ingreso: octubre-2014
Ubicación: Madrid
Mensajes: 1.212
Antigüedad: 9 años, 6 meses
Puntos: 204
Respuesta: Vector de punteros cuando muda a otro lado

Y por qué no usas shared_ptr o unique_ptr??

Si tienes claro que la vida de 's' siempre va a ser superior a los nodos del grafo puedes usar unique_ptr. Si la vida de los objetos pasa a ser más difusa entonces es más recomendable usar shared_ptr:

Ejemplo para unique_ptr:
Código C++:
Ver original
  1. vector<std::unique_ptr<SuperNode>> s(n);
  2. for( auto& node : s )
  3.   node = std::make_unique<SuperNode>();
  4.  
  5. // make_unique<> es un template que crea un unique_ptr ya inicializado
  6. // make_unique<SuperNode>()  es equivalente a
  7. // std::unique_ptr<SuperNode>(new SuperNode);
  8. // make_unique también admite parámetros:
  9. // make_unique<SuperNode>(1) es equivalente a
  10. // std::unique_ptr<SuperNode>(new SuperNode(1));

Y para que los nodos apunten al puntero:

Código C++:
Ver original
  1. class Node
  2. {
  3.   SuperNode* superNode;
  4. };
  5. node->superNode = s[0].get();

Opción con shared_ptr (1):

Código C++:
Ver original
  1. vector<std::shared_ptr<SuperNode>> s(n);
  2. for( auto& node : s )
  3.   node = std::make_shared<SuperNode>();

Y para referenciar el puntero:

Código C++:
Ver original
  1. class Node
  2. {
  3.   std::weak_ptr<SuperNode> superNode;
  4. };
  5.  
  6. node->superNode = s[0]; // La conversión a weak es implícita

¿Por qué usar weak? shared_ptr es una clase que gestiona un puntero que va a utilizarse en varias secciones del código. Lo que hace shared_ptr es mantener vivo el puntero mientras existan referencias al mismo (una referencia = una instancia de shared_ptr referenciando al puntero). Cuando ya no hay elementos shared_ptr referenciando el puntero la memoria es liberada de forma automática.

Si resulta que tienes una referencia circular (A referencia a B y B a A), entonces la memoria nunca se liberaría porque ambos punteros tendrían siempre una referencia.... en estos casos es donde entra en acción weak_ptr. weak_ptr representa una referencia débil a un puntero compartido. Es debil porque no impide que el puntero sea destruído (no cuenta como referencia). Para evitar problemas el propio weak_ptr incluye funcionalidad que indica si el puntero está vivo o no.

Un ejemplo ilustrativo:

Código C++:
Ver original
  1. void CrearShared(std::weak_ptr<int> weakPtr)
  2. {
  3.   if( std::shared_ptr<int> miShared = weakPtr.lock() )
  4.   {
  5.     std::cout << "El puntero esta referenciado " << weakPtr.use_count() << " vece(s)\n";
  6.     std::cout << "shared_ptr = " << *miShared << '\n';
  7.   }
  8.   else
  9.   {
  10.     std::cout << "El puntero ha sido eliminado\n";
  11.   }
  12. }
  13.  
  14. int main()
  15. {
  16.   std::weak_ptr<int> weak;
  17.  
  18.   {
  19.     std::shared_ptr<int> shared = std::make_shared<int>(4);
  20.  
  21.     weak = shared;
  22.  
  23.     std::cout << "El puntero esta referenciado " << weak.use_count() << " vece(s)\n";
  24.  
  25.     auto shared2 = shared;
  26.     std::cout << "El puntero esta referenciado " << weak.use_count() << " vece(s)\n";
  27.  
  28.     // Si el puntero está vivo podremos usarlo
  29.     CrearShared(weak);
  30.  
  31.     // Este mensaje muestra como el número de referencias ha bajado porque el shared_ptr
  32.     // que creamos dentro de la función ya no existe
  33.     std::cout << "El puntero esta referenciado " << weak.use_count() << " vece(s)\n";
  34.  
  35.   } // En este punto los dos shared_ptr serán eliminados, eso da lugar a que el puntero
  36.     // se elimine
  37.  
  38.   // Cuando ya no exista el puntero, weak_ptr nos avisará
  39.   CrearShared(weak);
  40. }

Un saludo
__________________
La ayuda se paga con esfuerzo o con dinero. Si no estás dispuesto a esforzarte y quieres que te hagan los deberes pide presupuesto, al menos así ahorrarás tiempo.
  #3 (permalink)  
Antiguo 26/09/2016, 05:24
 
Fecha de Ingreso: junio-2014
Mensajes: 144
Antigüedad: 9 años, 10 meses
Puntos: 1
Respuesta: Vector de punteros cuando muda a otro lado

Hola eferion. No me cansaré de agradecerte, siempre que tengo una duda y Google no me ayuda, tu me das una luz.
No conocía los shared_ptr ni los unique_ptr estudiaré que son y tu codigo para poder responderte gracias.
  #4 (permalink)  
Antiguo 26/09/2016, 06:46
 
Fecha de Ingreso: octubre-2014
Ubicación: Madrid
Mensajes: 1.212
Antigüedad: 9 años, 6 meses
Puntos: 204
Respuesta: Vector de punteros cuando muda a otro lado

Cita:
Iniciado por dmorill Ver Mensaje
No conocía los shared_ptr ni los unique_ptr.
Son lo que se conoce como punteros inteligentes. La gracia de estas clases es que se encargan de la gestión de un puntero, destruyéndolo cuando el mismo ya no es necesario.

unique_ptr es una clase que se encarga de la gestión de un puntero que no va a ser compartido. Cuando la ejecución abandona el ámbito de la variable el puntero se destruye automáticamente:

Código C++:
Ver original
  1. struct Test
  2. {
  3.   ~Test()
  4.   { std::cout << "Test::~Test()\n"; }
  5. }
  6.  
  7. void func()
  8. {
  9.   auto ptr = std::make_unique<Test>();
  10.   std::cout << "Despues de esto se llama al destructor\n";
  11. }
  12.  
  13. int main()
  14. {
  15.   func();
  16. }

Por supuesto un unique_ptr puede sacarse de una función (e incluso almacenarse en un contenedor). La única limitación (y es su razón de ser) es que el puntero no está compartido por otros punteros inteligentes:

Código C++:
Ver original
  1. std::unique_ptr<Test> func()
  2. {
  3.   return std::make_unique<Test>();
  4.   std::cout << "Aun no se llama al destructor\n";
  5. }
  6.  
  7. int main()
  8. {
  9.   auto ptr = func();
  10.   std::cout << "El objeto se destruye a continuacion\n";
  11. }

Para mover el puntero de una variable a otra... por ejemplo un bucle intermedio que pretende localizar un elemento en cuestión, se debe usar la sintaxis move (recuerda que al ser un elemento único no se puede llamar al constructor copia ni al de asignación alegremente):

Código C++:
Ver original
  1. void Print(std::unique_ptr<int>& var)
  2. {
  3.   if( var )
  4.     std::cout << *var << '\n';
  5.   else
  6.     std::cout << "Puntero vacio\n";
  7. }
  8.  
  9. int main()
  10. {
  11.   std::unique_ptr<int> a = std::make_unique<int>(2);
  12.   std::unique_ptr<int> b;
  13.  
  14.   Print(a); Print(b);
  15.  
  16.   //b = a; -> ERROR de compilación
  17.  
  18.   b = std::move(a); // sintaxis MOVE. a y b intercambian sus estados
  19.  
  20.   Print(a); Print(b);
  21. }

Gestionar los punteros con punteros inteligentes ayuda a reducir las fugas de memoria:
Código C++:
Ver original
  1. std::unique_ptr<Test> func()
  2. {
  3.   auto obj = std::make_unique<Test>();
  4.   throw 1; // Al lanzar la excepción el objeto se destruye automáticamente
  5.   return obj;
  6. }
  7.  
  8. int main()
  9. {
  10.   try
  11.   {
  12.     func();
  13.   }
  14.  
  15.   catch(...)
  16.   { }
  17. }

shared_ptr sigue la misma filosofía (proporcionar un mecanismo de gestión de la memoria) pero permite que el puntero esté compartido por varias variables. Lo que hace shared_ptr es invocar al destructor automáticamente cuando el puntero ya no tiene referencias.

El uso de shared_ptr requiere prestar atención a las referencias circulares. Recordemos que si un shared_ptr tiene al menos una referencia el objeto no se va a destruir, luego si A apunta a B y B a A, ninguno de los dos objetos se va a destruir.

Un saludo.
__________________
La ayuda se paga con esfuerzo o con dinero. Si no estás dispuesto a esforzarte y quieres que te hagan los deberes pide presupuesto, al menos así ahorrarás tiempo.
  #5 (permalink)  
Antiguo 27/09/2016, 06:48
 
Fecha de Ingreso: junio-2014
Mensajes: 144
Antigüedad: 9 años, 10 meses
Puntos: 1
Respuesta: Vector de punteros cuando muda a otro lado

Hola Eferion, según he leído y estudiado tus ejemplos creo que estos punteros inteligentes básicamente te ayudan a que no existan fugas de memoria. Estoy en lo cierto?

Pero el problema que tengo, no es solo que los punteros se queden apuntando a la nada (fugas de memoria) sino que me gustaría que apunten a donde el vector se mude automáticamente, porque hacerlo manual es muy largo estamos hablando de millones de punteros.

Es como si un puntero se queda "anclado" al vector y si éste se mueve, el puntero también. Se puede hacer algo así?

Saludos y gracias.
  #6 (permalink)  
Antiguo 27/09/2016, 07:02
Avatar de xKuZz  
Fecha de Ingreso: febrero-2015
Ubicación: nullptr
Mensajes: 183
Antigüedad: 9 años, 2 meses
Puntos: 27
Respuesta: Vector de punteros cuando muda a otro lado

Ten en cuenta qué, siguiendo la propuesta que te da eferion

Código C++:
Ver original
  1. std::vector<std::unique_ptr<SuperNode>> _neighbor;
  2. ...

Tienes un vector de punteros inteligentes que a apuntan a SuperNodos. Si aumenta la dimensión del vector y se cambia sus posiciones en memoria, lo que cambian son las posiciones de memoria dónde se guardan los punteros inteligentes que apuntan a los nodos, no la dirección de memoria en las que se guardan los SuperNodos.

Por lo cual mientras lo que vayas almacenando sea el puntero del SuperNodo(ya sea raw(*), shared o weak) en las otras estructuras o una referencia al SuperNodo y no un iterador o referencia sobre el vector no deberías de tener problema.
  #7 (permalink)  
Antiguo 27/09/2016, 07:06
 
Fecha de Ingreso: octubre-2014
Ubicación: Madrid
Mensajes: 1.212
Antigüedad: 9 años, 6 meses
Puntos: 204
Respuesta: Vector de punteros cuando muda a otro lado

xKuZz se me ha adelantado con la respuesta :).

Efectivamente los punteros inteligentes no son necesarios, pero entiendo que cuando creaste el vector de SuperNode elegiste meter los elementos por valor para despreocuparte de la memoria... luego te ofrecí una solución en la que no tuvieses que lidiar con la misma.

Por otro lado, manejar la memoria directamente es un tema muy delicado y es muy sencillo meter la pata. Hacer uso de herramientas que gestionen este apartado es altamente recomendable.

Un saludo
__________________
La ayuda se paga con esfuerzo o con dinero. Si no estás dispuesto a esforzarte y quieres que te hagan los deberes pide presupuesto, al menos así ahorrarás tiempo.
  #8 (permalink)  
Antiguo 29/09/2016, 15:44
 
Fecha de Ingreso: junio-2014
Mensajes: 144
Antigüedad: 9 años, 10 meses
Puntos: 1
Respuesta: Vector de punteros cuando muda a otro lado

Hola amigos, no he dejado olvidado el tema, sino que he estado con muchos cosas y sé que necesito una tarde para estudiar ésto. Así que gracias y ya les escribiré pronto.

Etiquetas: punteros, vectores
Atención: Estás leyendo un tema que no tiene actividad desde hace más de 6 MESES, te recomendamos abrir un Nuevo tema en lugar de responder al actual.
Respuesta




La zona horaria es GMT -6. Ahora son las 04:36.