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

 

 


Tema destacado: Usando Git para manipular el directorio de trabajo, el índice y commits (segunda parte)


+  Foro de elhacker.net
|-+  Programación
| |-+  Programación C/C++ (Moderadores: Eternal Idol, Littlehorse, K-YreX)
| | |-+  [C++][Consulta] Error inesperado con destructor
0 Usuarios y 1 Visitante están viendo este tema.
Páginas: [1] Ir Abajo Respuesta Imprimir
Autor Tema: [C++][Consulta] Error inesperado con destructor  (Leído 2,750 veces)
class_OpenGL


Desconectado Desconectado

Mensajes: 437

Si usas Direct3D, no eres mi amigo :P


Ver Perfil
[C++][Consulta] Error inesperado con destructor
« en: 3 Febrero 2017, 04:28 am »

Hola, muy buenas. Estoy realizando un árbol binario en C++. Lo estoy encapsulando en una clase. Como cualquier árbol binario, este tiene un dato, un puntero a la izquierda y otro a la derecha. El problema es que al llamar al destructor (el cual lo elimina), el programa finaliza brúscamente. El constructor es el siguiente:

Código
  1. template <typename T>
  2. class ArbolBinario {
  3.   private:
  4.      T dato;
  5.      ArbolBinario *izda;
  6.      ArbolBinario *dcha;
  7.  
  8.      void Eliminar(ArbolBinario *rama) {
  9.         if(NULL != rama->izda)
  10.            Eliminar(rama->izda);
  11.  
  12.         if(NULL != rama->dcha)
  13.            Eliminar(rama->dcha);
  14.  
  15.         delete rama;
  16.      }
  17.  
  18.   public:
  19.      [...]
  20.  
  21.      ~ArbolBinario() {
  22.         EliminarRamaIzda();
  23.         EliminarRamaDcha();
  24.      }
  25.  
  26.      [...]
  27.  
  28.      void EliminarRamaIzda() {
  29.         if(NULL != izda) {
  30.            Eliminar(izda);
  31.            izda = NULL;
  32.         }
  33.      }
  34.  
  35.      void EliminarRamaDcha() {
  36.         if(NULL != dcha) {
  37.            Eliminar(dcha);
  38.            dcha = NULL;
  39.         }
  40.      }
  41.  
  42.      [...]
  43. };

Este es la función main:

Código
  1. int main() {
  2.   ArbolBinario<int> arbol;
  3.  
  4.   arbol.SetDato(7, "");
  5.   arbol.ReemplazarRamaIzda(20, "");
  6.   arbol.ReemplazarRamaIzda(5, "0");
  7.   arbol.ReemplazarRamaDcha(7, "00");
  8.   arbol.ReemplazarRamaDcha(4, "0");
  9.   arbol.ReemplazarRamaIzda(1, "01");
  10.   arbol.ReemplazarRamaDcha(2, "01");
  11.   arbol.ReemplazarRamaDcha(8, "");
  12.   arbol.ReemplazarRamaIzda(10, "1");
  13.  
  14.   cout << arbol.ToString();
  15.  
  16.   return 0;
  17. }

Cuando uso el depurador, pongo un punto de ruptura en el destructor, y obtengo lo siguiente:

Código:
Breakpoint 1, ArbolBinario<int>::~ArbolBinario (this=0x28fddc, __in_chrg=<optimized out>) at main.cpp:84
84               EliminarRamaIzda();
(gdb) continue
Continuing.

Breakpoint 1, ArbolBinario<int>::~ArbolBinario (this=0x720fb0, __in_chrg=<optimized out>) at main.cpp:84
84               EliminarRamaIzda();
(gdb) continue
Continuing.

Breakpoint 1, ArbolBinario<int>::~ArbolBinario (this=0x7212f0, __in_chrg=<optimized out>) at main.cpp:84
84               EliminarRamaIzda();
(gdb) continue
Continuing.

Program received signal SIGSEGV, Segmentation fault.
0x004099bb in ArbolBinario<int>::Eliminar (this=0x0, arbol=0x726f58)
    at main.cpp:13
13            void Eliminar(ArbolBinario *arbol) {

Lo que no sé es: ¿por qué se llama 3 veces al constructor? En la última llamada, ¿por qué el puntero this vale 0 (supongo que por esto el programa crashea)?

Muchas gracias


En línea

Programador aficionado. Me quiero centrar en programar videojuegos. La API que uso para crearlos es OpenGL
MAFUS


Desconectado Desconectado

Mensajes: 1.603



Ver Perfil
Re: [C++][Consulta] Error inesperado con destructor
« Respuesta #1 en: 3 Febrero 2017, 07:22 am »

A mi entender duplicas las cosas.
En el destructor vas a borrar las dos ramas por separado pero estas funciones terminan apoyándose en Eliminar, que de igual forma vuelve a borrar a la izquierda y a la derecha por sí misma.
Tal vez debería ser al revés, que Eliminar estuviera a un nivel superior y fuera las que llamara a eliminar izquierda y derecha para borrar así el nodo completo.

De igual forma, tal y como lo tienes, solo EliminarRamaIzda y EliminarRamaDcha se encargan de llevar a NULL el puntero pero Eliminar no lo hace. ¿Llega el programa a un punto en que Eliminar pasará sobre un puntero que ya ha sido borrado previamente?


En línea

class_OpenGL


Desconectado Desconectado

Mensajes: 437

Si usas Direct3D, no eres mi amigo :P


Ver Perfil
Re: [C++][Consulta] Error inesperado con destructor
« Respuesta #2 en: 3 Febrero 2017, 08:51 am »

No entiendo muy bien eso de duplico. A ver, tengo el método Eliminar, que te elimina una rama del árbol. Este no comprueba que el árbol que le pases sea nulo (es una precondición que no será así, por así decirlo). Una vez que tengo ese método, creo otros dos que te eliminan la rama izquierda y la rama derecha, las cuales comprueban si la rama es válida (si esta no es nula). Una vez que tengo dos métodos que te eliminan dos ramas (sé que se podría haber resumido en una, pero me pareció útil para mis propósitos) tener métodos que te eliminen ramas independientes. El caso es que el constructor elimina el árbol formado por la rama izquierda y elimina el formado por la de la derecha:

Destructor:
    Elimina rama izquierda.
    Elimina rama derecha

No entiendo dónde está el conflicto.

________________________________________________________

He probado, desde el main, hacer lo del constructor, lo he eliminado y así si que funciona.

Nuevo main:

Código
  1. int main() {
  2.   ArbolBinario<int> arbol;
  3.  
  4.   arbol.SetDato(7, "");
  5.   arbol.ReemplazarRamaIzda(20, "");
  6.   arbol.ReemplazarRamaIzda(5, "0");
  7.   arbol.ReemplazarRamaDcha(7, "00");
  8.   arbol.ReemplazarRamaDcha(4, "0");
  9.   arbol.ReemplazarRamaIzda(1, "01");
  10.   arbol.ReemplazarRamaDcha(2, "01");
  11.   arbol.ReemplazarRamaDcha(8, "");
  12.   arbol.ReemplazarRamaIzda(10, "1");
  13.  
  14.   cout << arbol.ToString();
  15.  
  16.   arbol.EliminarRamaIzda();
  17.   arbol.EliminarRamaDcha();
  18.  
  19.   return 0;
  20. }

Por lo que alguna condición/requisito especial tiene que tener el destructor.

_____________________________________________________________


He probado a poner cout << "Algo" << endl en el destructor y esto sale:

Algo
Algo
Algo
Algo
Algo
Algo
Algo
Algo
Algo

¿Por qué se llama tantas veces?

______________________________________________________________


Soy nuevo en C++ (vengo de C), ¿delete Objeto llama al destructor del objeto?
« Última modificación: 3 Febrero 2017, 09:24 am por class_OpenGL » En línea

Programador aficionado. Me quiero centrar en programar videojuegos. La API que uso para crearlos es OpenGL
MAFUS


Desconectado Desconectado

Mensajes: 1.603



Ver Perfil
Re: [C++][Consulta] Error inesperado con destructor
« Respuesta #3 en: 3 Febrero 2017, 14:24 pm »

Si pudieras pasar tu código entero para probar mejor.

La idea es:
Código
  1. static void Eliminar(ArbolBinario **raiz) {
  2.    // como no soy de C++ no sé si aquí el operador &
  3.    // en el argumento ArbonBinario &rama te podría funcionar
  4.    if(*raiz) {
  5.        delete *raiz;
  6.        *raiz = NULL;
  7.    }
  8. }
  9.  
  10. void EliminarRamaIzda() {
  11.    if(!izda) {
  12.        delete izda;
  13.        izda = NULL;
  14.    }
  15. }
  16.  
  17. void EliminarRamaDcha() {
  18.    if(!dcha) {
  19.        delete dcha;
  20.        dcha = NULL;
  21.    }
  22. }
  23.  
  24. ~ArbolBinario() {
  25.    EliminarRamaIzda();
  26.    EliminarRamaDcha();
  27. }
  28.  

Lo dicho, si tuviera el código completo del árbol podría probarlo, pero por ahora esa es mi idea. Puede tener una infinidad de bugs, código no probado.
« Última modificación: 3 Febrero 2017, 14:30 pm por MAFUS » En línea

ivancea96


Desconectado Desconectado

Mensajes: 3.412


ASMático


Ver Perfil WWW
Re: [C++][Consulta] Error inesperado con destructor
« Respuesta #4 en: 3 Febrero 2017, 14:35 pm »

Código
  1. void Eliminar(ArbolBinario *rama) {
  2.   if(NULL != rama->izda)
  3.      Eliminar(rama->izda);
  4.  
  5.  if(NULL != rama->dcha)
  6.      Eliminar(rama->dcha);
  7.  
  8.   delete rama;
  9. }

Eliminar no establece las ramas a NULL. COn esta función recursiva, estás eliminando todas las ramas, pero de manera incorrecta:
Primero, liberas la memoria de cada lado de la rama. Luego, llamas al destructor de la rama y la liberas. El destructor de la rama, ya llama a Eliminar. Solo que ahora, llama a Eliminar sobre unas ramas que no apuntan a nada válido.
En línea

class_OpenGL


Desconectado Desconectado

Mensajes: 437

Si usas Direct3D, no eres mi amigo :P


Ver Perfil
Re: [C++][Consulta] Error inesperado con destructor
« Respuesta #5 en: 3 Febrero 2017, 18:19 pm »

Sii con el gdb (y mucho esfuerzo, por fin me entere del problema. Con C++ hay que tener un cuidado especial, tanto o mas que con C xD (supongo que sera cuestion de muucha practica, como siempre)

Muchas gracias por sus respuestas!!
En línea

Programador aficionado. Me quiero centrar en programar videojuegos. La API que uso para crearlos es OpenGL
Páginas: [1] Ir Arriba Respuesta Imprimir 

Ir a:  

WAP2 - Aviso Legal - Powered by SMF 1.1.21 | SMF © 2006-2008, Simple Machines