elhacker.net cabecera Bienvenido(a), Visitante. Por favor Ingresar o Registrarse
¿Perdiste tu email de activación?.

 

 


Tema destacado: Rompecabezas de Bitcoin, Medio millón USD en premios


+  Foro de elhacker.net
|-+  Programación
| |-+  Programación C/C++ (Moderadores: Eternal Idol, Littlehorse, K-YreX)
| | |-+  [Resuelto] Duda con destructores - vector de una clase
0 Usuarios y 1 Visitante están viendo este tema.
Páginas: [1] Ir Abajo Respuesta Imprimir
Autor Tema: [Resuelto] Duda con destructores - vector de una clase  (Leído 3,353 veces)
X3R4CK3R

Desconectado Desconectado

Mensajes: 74


Divide y vencerás


Ver Perfil WWW
[Resuelto] Duda con destructores - vector de una clase
« en: 15 Julio 2013, 15:16 pm »

Buenas, repasando el tema de los destructores me he topado con un bache, algo que intuyo que es provocado por std::vector, me explico:

Tengo una clase "Padre" con un vector de clases "Hijos", cada clase Hijo tiene un destructor que indica la "muerte" de ésta. A partir del código que publico a continuación, esperaba que ningún hijo muriese a no ser que le aplique un delete o el programa finalizase, pero no es así.
Cada vez que hago un push_back a un Hijo desde la clase Padre, los Hijos que ya habían anteriormente en el vector son destruidos, ¿por qué?  :huh:

Mirando la documentación oficial de std::vector::push_back, me encuentro con:

Citar
This effectively increases the container size by one, which causes an automatic reallocation of the allocated storage space if -and only if- the new vector size surpasses the current vector capacity.

Por lo que entiendo de ahí, el hecho de reubicar memoria, podría causar que se destruya la clase Hijo del vector y se vuelva a construir, pero si así fuese, ¿por qué no se ejecuta el cout que notifica que ha nacido la clase Hijo?


class.hpp
Código
  1. #include <iostream>
  2. #include <vector>
  3. using namespace std;
  4.  
  5. class Child
  6. {
  7.    string name;
  8. public:
  9.    Child(string);
  10.    ~Child();
  11.    string getName();
  12. };
  13.  
  14. class Father
  15. {
  16.    vector<Child> children;
  17. public:
  18.    void addChild(Child*);
  19.    void showNames();
  20. };

class.cpp
Código
  1. #include "class.hpp"
  2.  
  3. Child::Child(string n) : name(n)
  4. {
  5.    cout << "Ha nacido " << name << "." << endl;
  6. }
  7.  
  8. Child::~Child()
  9. {
  10.    cout << "Ha muerto " << name << "." << endl;
  11. }
  12.  
  13. string Child::getName()
  14. {
  15.    return name;
  16. }
  17.  
  18. void Father::addChild(Child *c)
  19. {
  20.    cout << c->getName() << " ha sido adoptado." << endl;
  21.    children.push_back(*c);
  22. }
  23.  
  24. void Father::showNames()
  25. {
  26.    for(unsigned int i=0; i<children.size(); i++)
  27.    {
  28.        cout << "Hijo #" << i << ": " << children.at(i).getName() << endl;
  29.    }
  30. }

main.cpp
Código
  1. #include "class.hpp"
  2.  
  3. int main(int argc, char *argv[])
  4. {
  5.    Child *c1, *c2, *c3;
  6.    c1 = new Child("Juan");
  7.    c2 = new Child("Lucas");
  8.    c3 = new Child("Antonio");
  9.  
  10.    Father f1;
  11.  
  12.    cout << "---" << endl;
  13.    f1.addChild(c1);
  14.    cout << "---" << endl;
  15.    f1.addChild(c2);
  16.    cout << "---" << endl;
  17.    f1.addChild(c3);
  18.    cout << "---" << endl;
  19.  
  20.    f1.showNames();
  21.  
  22.    delete c2;
  23.    delete c3;
  24.  
  25.    f1.showNames();
  26.  
  27.    cout << "---" << endl;
  28.    return 0;
  29. }

Output
Citar
Ha nacido Juan.
Ha nacido Lucas.
Ha nacido Antonio.
---
Juan ha sido adoptado.
---
Lucas ha sido adoptado.
Ha muerto Juan.
---
Antonio ha sido adoptado.
Ha muerto Juan.
Ha muerto Lucas.
---
Hijo #0: Juan
Hijo #1: Lucas
Hijo #2: Antonio
Ha muerto Lucas.
Ha muerto Antonio.
Hijo #0: Juan
Hijo #1: Lucas
Hijo #2: Antonio
---
Ha muerto Juan.
Ha muerto Lucas.
Ha muerto Antonio.

Mientras creaba este post, me he dado cuenta de que estaba haciendo un vector de Hijos (vector<Child>), cuando lo que quería hacer es un vector de punteros a clases Hijos (vector<Child*>).
Ésto soluciona el programa que arriba expongo, pero sigo con la duda de por qué, si no es un vector de punteros, ocurre lo arriba explicado.

Aparte de esta duda, una de las metas de éste código es que al hacerle delete a una clase Hijo, ésta sea borrada del vector<Hijos> de la clase Padre, cosa que creo que no es posible hacer directamente, (o tal vez sí, por eso pregunto), de no ser posible, agradecería cualquier método de "actualización", "verificación" o similar, que detecte que un miembro del vector de punteros ha sido borrado, y también debe ser borrado del vector... No sé si me explico. :X

Saludos


« Última modificación: 15 Julio 2013, 17:27 pm por X3R4CK3R » En línea

0xDani


Desconectado Desconectado

Mensajes: 1.077



Ver Perfil
Re: Duda con destructores - vector de una clase
« Respuesta #1 en: 15 Julio 2013, 15:53 pm »

Claro, lo ideal es que tengas un vector de punteros a los hijos.

En cuanto a esto:

Cita de: X3R4CK3R
una de las metas de éste código es que al hacerle delete a una clase Hijo, ésta sea borrada del vector<Hijos> de la clase Padre

Lo que puedes hacer es que, al incluir un hijo en la lista, el padre notifique al hijo de que ha sido adoptado, y este guarde la dirección de su padre. Luego, al ser destruido el hijo, que llame a una funcion removeChild() (que habrás de implementar, y que eliminará a un hijo de la lista) con su propia dirección.


En línea

I keep searching for something that I never seem to find, but maybe I won't, because I left it all behind!

I code for $$$
Hago trabajos en C/C++
Contactar por PM
amchacon


Desconectado Desconectado

Mensajes: 1.211



Ver Perfil
Re: Duda con destructores - vector de una clase
« Respuesta #2 en: 15 Julio 2013, 16:04 pm »

Citar
This effectively increases the container size by one, which causes an automatic reallocation of the allocated storage space if -and only if- the new vector size surpasses the current vector capacity.
Esto no te influye, el recolocamiento es completamente transparente.

El problema esque estás creando copiando los objetos cada vez que haces push_back, por eso al hacer esto:

Código
  1. delete c2;
  2. delete c3;

No tiene ningún efecto, tienes que usar punteros para apuntar a estos objetos:

Código
  1. vector<Child*> children;
En línea

Por favor, no me manden MP con dudas. Usen el foro, gracias.

¡Visita mi programa estrella!

Rar File Missing: Esteganografía en un Rar
X3R4CK3R

Desconectado Desconectado

Mensajes: 74


Divide y vencerás


Ver Perfil WWW
Re: Duda con destructores - vector de una clase
« Respuesta #3 en: 15 Julio 2013, 16:25 pm »

El problema esque estás creando copiando los objetos cada vez que haces push_back, por eso al hacer esto:

Código
  1. delete c2;
  2. delete c3;

No tiene ningún efecto, tienes que usar punteros para apuntar a estos objetos:

Código
  1. vector<Child*> children;
Mientras creaba este post, me he dado cuenta de que estaba haciendo un vector de Hijos (vector<Child>), cuando lo que quería hacer es un vector de punteros a clases Hijos (vector<Child*>).
Sí, lo sé, fue solo un descuido, ya corregí el código antes de postearlo, pero no consideré conveniente postear el código actualizado, ya que la duda que me ha surgido ocurre cuando los elementos no son punteros, todos los elementos (no punteros) se destruyen automáticamente al llegar al final del bracket '}' donde se encuentra declarado, pero ¿por qué al hacer un push_back, el resto de elementos que ya estaban en el vector son destruídos? o al menos, la función destructor es ejecutada. No lo entiendo. :/

Me da la impresión de que no me estoy explicando bien, pero no sé como explicarlo mejor, no es algo que necesite resolver, pero es una duda que me desconcierta y quiero aclarar.  :rolleyes:

Claro, lo ideal es que tengas un vector de punteros a los hijos.

En cuanto a esto:

Lo que puedes hacer es que, al incluir un hijo en la lista, el padre notifique al hijo de que ha sido adoptado, y este guarde la dirección de su padre. Luego, al ser destruido el hijo, que llame a una funcion removeChild() (que habrás de implementar, y que eliminará a un hijo de la lista) con su propia dirección.

A mí se me había ocurrido algo parecido:
Al llamar a Father.addChild, guardar en el hijo adoptado la dirección del vector de hijos (miembro de la clase Father), y simplemente, cuando un hijo sea borrado, llamar a la función erase del vector, sería un método directo y muy eficaz, el problema está en hacer un puntero al vector, de primera mano no se me ocurre como hacerlo, he probado con templates pero nada... supongo que tendré que dar algún rodeo y hacerlo como bien has dicho.

Gracias a ambos, un saludo! :)
En línea

amchacon


Desconectado Desconectado

Mensajes: 1.211



Ver Perfil
Re: Duda con destructores - vector de una clase
« Respuesta #4 en: 15 Julio 2013, 16:34 pm »

¿por qué al hacer un push_back, el resto de elementos que ya estaban en el vector son destruídos? o al menos, la función destructor es ejecutada. No lo entiendo. :/

Me da la impresión de que no me estoy explicando bien, pero no sé como explicarlo mejor, no es algo que necesite resolver, pero es una duda que me desconcierta y quiero aclarar.  :rolleyes:
Los mensajes del destructor no vienen del push_back sino de los delete que has puesto.
En línea

Por favor, no me manden MP con dudas. Usen el foro, gracias.

¡Visita mi programa estrella!

Rar File Missing: Esteganografía en un Rar
X3R4CK3R

Desconectado Desconectado

Mensajes: 74


Divide y vencerás


Ver Perfil WWW
Re: Duda con destructores - vector de una clase
« Respuesta #5 en: 15 Julio 2013, 16:40 pm »

Los mensajes del destructor no vienen del push_back sino de los delete que has puesto.

No, me refiero a los siguientes:
Citar
Ha nacido Juan.
Ha nacido Lucas.
Ha nacido Antonio.
---
Juan ha sido adoptado. (push_back(c1))
---
Lucas ha sido adoptado. (push_back(c2))
Ha muerto Juan. (c1 muere por push_back(c2))
---
Antonio ha sido adoptado. (push_back(c3))
Ha muerto Juan. (c1 muere por push_back(c3))
Ha muerto Lucas. (c2 muere por push_back(c3))
---
Hijo #0: Juan
Hijo #1: Lucas
Hijo #2: Antonio
Ha muerto Lucas. (Muerte por delete)
Ha muerto Antonio. (Muerte por delete)
Hijo #0: Juan
Hijo #1: Lucas
Hijo #2: Antonio
---
Ha muerto Juan.
Ha muerto Lucas.
Ha muerto Antonio.

Ahí no hay ningún delete. :huh:



Por otra parte, ya he logrado mi propósito y quiero publicar a continuación el código para compartirlo:

class.hpp
Código
  1. #include <iostream>
  2. #include <vector>
  3. using namespace std;
  4.  
  5. class Father;
  6.  
  7. class Child
  8. {
  9.    string name;
  10.    Father *p;
  11. public:
  12.    Child(string);
  13.    ~Child();
  14.    string getName();
  15.    void assignFather(Father *);
  16. };
  17.  
  18. class Father
  19. {
  20.    vector<Child*> children;
  21. public:
  22.    void addChild(Child *);
  23.    void removeChild(Child *);
  24.    void showNames();
  25. };

class.cpp
Código
  1. #include "class.hpp"
  2.  
  3. Child::Child(string n) : name(n)
  4. {
  5.    cout << "Ha nacido " << name << "." << endl;
  6. }
  7.  
  8. Child::~Child()
  9. {
  10.    cout << "Ha muerto " << name << "." << endl;
  11.    p->removeChild(this);
  12. }
  13.  
  14. string Child::getName()
  15. {
  16.    return name;
  17. }
  18.  
  19. void Child::assignFather(Father *p)
  20. {
  21.    this->p = p;
  22. }
  23.  
  24. void Father::addChild(Child *c)
  25. {
  26.    c->assignFather(this);
  27.    children.push_back(c);
  28.    cout << c->getName() << " ha sido adoptado." << endl;
  29. }
  30.  
  31. void Father::removeChild(Child *c)
  32. {
  33.    for(unsigned int i=0; i<children.size(); i++)
  34.    {
  35.        if(c==children.at(i))
  36.        {
  37.            children.erase(children.begin()+i);
  38.            cout << "Hijo #" << i << " (" << c->getName() << ") ha muerto." << endl;
  39.            break;
  40.        }
  41.    }
  42. }
  43.  
  44. void Father::showNames()
  45. {
  46.    cout << "---" << endl
  47.         << "Numero de hijos: " << children.size() << endl;
  48.    for(unsigned int i=0; i<children.size(); i++)
  49.    {
  50.        cout << "Hijo #" << i << ": " << children.at(i)->getName() << endl;
  51.    }
  52.    cout << "---" << endl;
  53. }

main.cpp
Código
  1. #include "class.hpp"
  2.  
  3. int main()
  4. {
  5.    Child *c1, *c2, *c3;
  6.    c1 = new Child("Juan");
  7.    c2 = new Child("Lucas");
  8.    c3 = new Child("Antonio");
  9.  
  10.    Father f1;
  11.  
  12.    f1.addChild(c1);
  13.    f1.addChild(c2);
  14.    f1.addChild(c3);
  15.  
  16.    f1.showNames();
  17.  
  18.    delete c2;
  19.  
  20.    f1.showNames();
  21.  
  22.    return 0;
  23. }

Output:
Citar
Ha nacido Juan.
Ha nacido Lucas.
Ha nacido Antonio.
Juan ha sido adoptado.
Lucas ha sido adoptado.
Antonio ha sido adoptado.
---
Numero de hijos: 3
Hijo #0: Juan
Hijo #1: Lucas
Hijo #2: Antonio
---
Ha muerto Lucas.
Hijo #1 (Lucas) ha muerto.
---
Numero de hijos: 2
Hijo #0: Juan
Hijo #1: Antonio
---

La utilidad de este código puede ser amplia, en mi caso, estoy desarrolando una Gui para SFML, donde un elemento o Widget(Button, TextBox...) sería la clase hijo y una capa(Layout), la clase padre, que contiene el vector de Widgets.

Un saludo
« Última modificación: 15 Julio 2013, 17:01 pm por X3R4CK3R » En línea

amchacon


Desconectado Desconectado

Mensajes: 1.211



Ver Perfil
Re: Duda con destructores - vector de una clase
« Respuesta #6 en: 15 Julio 2013, 17:05 pm »

Mm... Pues vaya sorpresa  :-\

He hecho una miniprueba:

Código
  1. #include <iostream>
  2. #include <vector>
  3.  
  4. class A
  5. {
  6.   public:
  7.    int cosa;
  8.  
  9.    A(int i) : cosa(i) { std::cout<<"Constructor invocado: "<<i<<std::endl;}
  10.    A(const A & b){std::cout<<"Constructor copia invocado: "<<b.cosa<<std::endl; this->cosa = b.cosa;}
  11.    ~A() { std::cout<<"Destructor invocado: "<<cosa<<std::endl;}
  12. };
  13. int main()
  14. {
  15.    A Buffer[5] { 0,1,2,3,4};
  16.    std::vector<A> Cosas;
  17.    std::cout<<"Empezando... "<<std::endl<<std::endl;
  18.  
  19.    for (short i = 0; i < 5;i++)
  20.        Cosas.push_back(Buffer[i]);
  21.  
  22.    std::cin.get();
  23.    return 0;
  24. }

Te va diciendo los objetos que se van destruyendo, se construyen o se copian... Al parecer a partir de los 3 objetos, el vector intenta recolocar la memoria y para ello tiene que destruir los objetos y volverlos a construir en otra parte.

Pues parece que tenías razón  ;). Por curiosidad hice la prueba con listas:

Código
  1. #include <iostream>
  2. #include <list>
  3.  
  4. class A
  5. {
  6.   public:
  7.    int cosa;
  8.  
  9.    A(int i) : cosa(i) { std::cout<<"Constructor invocado: "<<i<<std::endl;}
  10.    A(const A & b){std::cout<<"Constructor copia invocado: "<<b.cosa<<std::endl; this->cosa = b.cosa;}
  11.    ~A() { std::cout<<"Destructor invocado: "<<cosa<<std::endl;}
  12. };
  13. int main()
  14. {
  15.    A Buffer[5] { 0,1,2,3,4};
  16.    std::list<A> Cosas;
  17.    std::cout<<"Empezando... "<<std::endl<<std::endl;
  18.  
  19.    for (short i = 0; i < 5;i++)
  20.        Cosas.push_back(Buffer[i]);
  21.  
  22.    std::cin.get();
  23.    return 0;
  24. }

Aquí no pasa, de modo que está claro que es por el realloc() que se hace.
En línea

Por favor, no me manden MP con dudas. Usen el foro, gracias.

¡Visita mi programa estrella!

Rar File Missing: Esteganografía en un Rar
0xDani


Desconectado Desconectado

Mensajes: 1.077



Ver Perfil
Re: Duda con destructores - vector de una clase
« Respuesta #7 en: 15 Julio 2013, 17:07 pm »

@X3R4CK3R, veo que has entendido a la primera lo que te dije y ha funcionado, me alegra haber sido de ayuda  :)
En línea

I keep searching for something that I never seem to find, but maybe I won't, because I left it all behind!

I code for $$$
Hago trabajos en C/C++
Contactar por PM
X3R4CK3R

Desconectado Desconectado

Mensajes: 74


Divide y vencerás


Ver Perfil WWW
Re: Duda con destructores - vector de una clase
« Respuesta #8 en: 15 Julio 2013, 17:25 pm »

@amchacon: mm, cierto, no recordaba eso del constructor copia, eso era lo que no encajaba en mi teoría, lo que no entiendo es el orden en el que se van copiando y destruyendo los elementos, pero bueno, supongo que dependerá de los bloques de memoria que hayan libres y tal, así que mejor no darle más vueltas. :laugh: Gracias por la aclaración.

@0xDani: Sí, gracias, aunque la verdad es que ya había pensado en ese método, pero antes de desarrollarlo quise dar un paso más y hacerlo de forma más directa, suelo darle muchas vueltas a todo para hacerlo de la forma más eficiente posible. De cualquier forma, el método funciona y puedo trabajar con él.

Gracias a ambos y un saludo!  ;)
« Última modificación: 15 Julio 2013, 17:30 pm por X3R4CK3R » En línea

Páginas: [1] Ir Arriba Respuesta Imprimir 

Ir a:  

Mensajes similares
Asunto Iniciado por Respuestas Vistas Último mensaje
[C++] Buscar un dato privado en un vector de clase
Programación C/C++
Rockmore 7 5,987 Último mensaje 10 Marzo 2011, 18:13 pm
por Rockmore
clase Vector
Java
m@o_614 1 1,812 Último mensaje 5 Junio 2012, 22:41 pm
por тαптяα
[SOLUCIONADO] Duda con destructores en C++ (que borrar y que no)
Programación C/C++
SARGE553413 2 2,057 Último mensaje 16 Agosto 2014, 16:57 pm
por SARGE553413
duda con sobrecarga de operadores en clase vector [c++]
Programación C/C++
andoporto 1 3,266 Último mensaje 5 Diciembre 2014, 08:36 am
por eferion
Vector de una clase
Java
user-marcos 2 1,669 Último mensaje 18 Julio 2015, 21:28 pm
por user-marcos
WAP2 - Aviso Legal - Powered by SMF 1.1.21 | SMF © 2006-2008, Simple Machines