¿Este destructor de C++ es redundante?

4 minutos de lectura

avatar de usuario
Simón

Recibí un código C++ con varias estructuras definidas así:

typedef struct _someStruct_ {
   std::string someString; 
   std::vector<std::string> someVectorOfStrings;
   int  someOtherStuff;

   ~_someStruct_()
   {
      someString.clear();
      someVectorOfStrings.clear(); 
   }
} someStruct; 

¿El destructor aquí es completamente redundante? Si la estructura fuera destruida por el destructor predeterminado, ¿no se destruirían cadenas, vectores, etc. de todos modos?

Si hubiera escrito el código, no habría pensado en agregar un destructor explícito aquí; simplemente dejaría que el compilador siguiera adelante.

Tal como lo entiendo, la única vez que podría necesitar crear su propio destructor en una estructura es si alguno de los miembros de las estructuras contiene punteros a datos que podrían necesitar limpieza, o si alguna funcionalidad adicional (por ejemplo, para depurar, iniciar sesión cuando se elimina una estructura) es necesario.

¿Me estoy perdiendo algo aquí? ¿Hay alguna razón por la que las cadenas y los vectores se hayan borrado explícitamente en el destructor? Mi sospecha es que la persona que me envió esto es realmente un programador de C (cf. typedef) que intentó convertir un código C en C++.

  • Sí, es completamente inútil. Sin embargo, si el vector hubiera estado sosteniendo punteros sin procesar, tendría que llamar a eliminarse en cada uno de los punteros (momento en el que debería usar punteros inteligentes).

    – Borglíder

    3 oct 2014 a las 14:36


  • mientras los destructores de los miembros hagan lo que se necesita, no hay necesidad de un destructor personalizado.

    –Karoly Horvath

    3 oct 2014 a las 14:38


  • typedef _someStruct_ { ... } someStruct; también es completamente redundante, solo escribe struct someStruct { ... };. Dado que el tipo no puede ser válido en un programa C (porque usa un destructor), no hay razón alguna para usar un typedef para el nombre de la estructura.

    –Jonathan Wakely

    3 oct 2014 a las 15:54


  • Si una de las respuestas respondió a su pregunta, márquela como aceptada.

    – JengibrePlusPlus

    4 oct 2014 a las 13:36

  • Por cierto, este destructor cambia el orden de limpieza de cadenas y vectores.

    – magras

    19 de octubre de 2014 a las 12:16

avatar de usuario
Konrad Rodolfo

Sí, el destructor es completamente redundante.

Como usted mismo ha dicho, el código tiene otras señales de advertencia. Usando typedef struct por ejemplo, no tiene ningún sentido en C++, es tan redundante como el destructor vacío: el código fue escrito por alguien con una comprensión marginal de C++, es probable que haya más trampas (por un lado, el nombre de la clase no es válido debido a la guion bajo inicial en alcance global).

  • Espera, ¿no tiene que ser un guión bajo inicial? seguido de un carácter en mayúscula?

    –Bartek Banachewicz

    3 oct 2014 a las 15:51

  • @JonathanWakely Oh, maldita sea, ahora lo entiendo. Culpa mía.

    –Bartek Banachewicz

    3 oct 2014 a las 15:53

  • typedef struct tagname {} type es muy común en los encabezados que tienen que ser compatibles tanto con C como con C++ con respecto a los compiladores anteriores a 1996 (principalmente debido al hecho de que las etiquetas de estructura de C no compartían el espacio de nombres de tipo, pero C++ sí). <user.h> incluido en <windows.h> tienen un gran uso de esta técnica que permite que esas definiciones funcionen intactas desde 1994 hasta la actualidad. Un muy buen ejemplo del principio de “no te repitas”. No seas tan cruel en el juicio moral.

    –Emilio Garavaglia

    04/10/2014 a las 17:21


  • @Emilio Eso simplemente no es aplicable aquí ya que el código no es un código C válido de todos modos.

    – Konrad Rodolfo

    4 oct 2014 a las 18:09

De hecho, esto es peor que simplemente usar el destructor implícito.

¡Al tener el destructor explícito, el compilador no le proporcionará un constructor de movimiento implícito!

El destructor es casi completamente redundante.

Hace tres cosas.

Primero, bloquea la creación automática de constructores y asignaciones de copiar/mover. Esto… puede que no sea algo bueno. Pero tal vez sea deseado. Sin embargo, no es lo mismo que no estar allí.

En segundo lugar, cambia el orden en que se limpian las cosas. El búfer contenido por la cadena se borra, luego cada búfer contenido por las cadenas en el vector se destruye mientras que la cadena que los contiene se destruye, luego el vector con un búfer de no utilizado se destruye la memoria (devolviendo la memoria), luego se destruye la cadena ahora vacía.

Con un destructor predeterminado, el orden es que se destruyen los búferes de la cadena del vector, luego se devuelve la memoria para las cadenas del vector mientras se destruye el vector, después la cadena se destruye junto con su búfer devuelto.

Posiblemente podría detectar esto con sobrecargas adecuadas para el operador new & deleteo si usó asignadores personalizados.

Finalmente, tales destructores a veces pueden ser más fáciles de depurar, ya que puede pasar por ellos.

Sin embargo, lo más probable es que ninguno de estos efectos sutiles haya sido previsto por el desarrollador que escribió ese código.

  • Puede ser bueno pensar en la mecánica, el destructor predeterminado puede desasignar pero no “borrar”. De hecho, ni siquiera estoy seguro de que borrar y borrar explícitamente cero posiciones en exceso. Es posible que no queramos que se raspe la memoria, ni siquiera la memoria no asignada.

    – mckenzm

    21 de marzo de 2015 a las 20:39

¿Ha sido útil esta solución?