Saltar al contenido

Destructor de C ++ con retorno

Nuestros desarrolladores estrellas han agotado sus provisiones de café, investigando a tiempo completo por la solución, hasta que Xavier encontró la contestación en Gogs así que hoy la comparte aquí.

Solución:

No, no puede evitar que el objeto sea destruido por la declaración de retorno, solo significa que la ejecución del cuerpo del dtor terminará en ese punto. Después de eso, aún será destruido (incluidos sus miembros y bases), y la memoria aún será desasignada.

Usted lanza una excepción.

Class2::~Class2() noexcept(false) 
    if (status != FINISHED) throw some_exception();


Class1::~Class1() 
    myClass2->status = FINISHED;
    try 
        delete myClass2;
     catch (some_exception& e) 
        // what should we do now?
    

Tenga en cuenta que es un terrible idea de hecho. Será mejor que reconsideres el diseño, estoy seguro de que debe haber uno mejor. Lanzar una excepción no detendrá la destrucción de sus bases y miembros, solo permitirá obtener el resultado del proceso de Class2dtor. Y todavía no está claro qué se podría hacer con él.

~Foo()
   return;

significa exactamente lo mismo que:

~Foo() 

Es similar a un void función; llegando al final sin un return; declaración es lo mismo que tener return; al final.

El destructor contiene acciones que se realizan cuando el proceso de destrucción de un Foo ya ha comenzado. No es posible abortar un proceso de destrucción sin abortar todo el programa.

[D]¿Volver explícitamente del destructor significa que no queremos destruirlo nunca?

No. Un regreso anticipado (a través de return; o throw ...) solo significa que el resto del cuerpo del destructor no se ejecuta. La base y los miembros aún están destruidos y sus destructores aún funcionan, consulte [except.ctor]/ 3.

Para un objeto de tipo de clase de cualquier duración de almacenamiento cuya inicialización o destrucción termina con una excepción, el destructor se invoca para cada uno de los subobjetos completamente construidos del objeto ...

Vea a continuación ejemplos de código de este comportamiento.


Quiero hacer que un determinado objeto se destruya solo a través de otro destructor de objetos, es decir, solo cuando el otro objeto esté listo para ser destruido.

Parece que la pregunta tiene sus raíces en el tema de la propiedad. Eliminar el objeto "poseído" solo una vez que el padre se destruye en un idioma muy común y se logra con uno de (pero no limitado a);

  • Composición, es una variable miembro automática (es decir, "basada en pila")
  • A std::unique_ptr<> para expresar la propiedad exclusiva del objeto dinámico
  • A std::shared_ptr<> para expresar la propiedad compartida de un objeto dinámico

Dado el ejemplo de código en el OP, el std::unique_ptr<> puede ser una alternativa adecuada;

class Class1 
  // ...
  std::unique_ptr myClass2;
  // ...
;

Class1::~Class1() 
    myClass2->status = FINISHED;
    // do not delete, the deleter will run automatically
    // delete myClass2;


Class2::~Class2() 
    //if (status != FINISHED)
    //  return;
    // We are finished, we are being deleted.

Noto el if verificación de condición en el código de ejemplo. Sugiere que el estado está vinculado a la propiedad y la vida. No son todos lo mismo; seguro, puede vincular el objeto que alcanza un cierto estado a su vida "lógica" (es decir, ejecutar algún código de limpieza), pero evitaría el enlace directo a la propiedad del objeto. Puede ser una mejor idea reconsiderar parte de la semántica involucrada aquí, o permitir que la construcción "natural" y la destrucción dicten los estados de inicio y fin del objeto.

Nota al margen; si tiene que verificar algún estado en el destructor (o afirmar alguna condición final), una alternativa al throw es llamar std::terminate (con algunos registros) si esa condición no se cumple. Este enfoque es similar al comportamiento estándar y resulta cuando se lanza una excepción al desenrollar la pila como resultado de una excepción ya lanzada. Este es también el comportamiento estándar cuando un std::thread sale con una excepción no controlada.


[D]¿Volver explícitamente del destructor significa que no queremos destruirlo nunca?

No (ver arriba). El siguiente código demuestra este comportamiento; vinculado aquí y una versión dinámica. los noexcept(false) es necesario para evitar std::terminate() siendo llamado.

#include 
using namespace std;
struct NoisyBase 
    NoisyBase()  cout << __func__ << endl; 
    ~NoisyBase()  cout << __func__ << endl; 
    NoisyBase(NoisyBase const&)  cout << __func__ << endl; 
    NoisyBase& operator=(NoisyBase const&)  cout << __func__ << endl; return *this;     
;
struct NoisyMember 
    NoisyMember()  cout << __func__ << endl; 
    ~NoisyMember()  cout << __func__ << endl; 
    NoisyMember(NoisyMember const&)  cout << __func__ << endl; 
    NoisyMember& operator=(NoisyMember const&)  cout << __func__ << endl; return *this;     
;
struct Thrower : NoisyBase 
    Thrower()  cout << __func__ << std::endl; 
    ~Thrower () noexcept(false) 
        cout << "before throw" << endl;
        throw 42;
        cout << "after throw" << endl;
    
    NoisyMember m_;
;
struct Returner : NoisyBase 
    Returner()  cout << __func__ << std::endl; 
    ~Returner () noexcept(false) 
        cout << "before return" << endl;
        return;
        cout << "after return" << endl;
    
    NoisyMember m_;
;
int main()

    try 
        Thrower t;
    
    catch (int& e) 
        cout << "catch... " << e << endl;
    
    
        Returner r;
    

Tiene la siguiente salida;

NoisyBase
NoisyMember
Thrower
before throw
~NoisyMember
~NoisyBase
catch... 42
NoisyBase
NoisyMember
Returner
before return
~NoisyMember
~NoisyBase

valoraciones y reseñas

¡Haz clic para puntuar esta entrada!
(Votos: 0 Promedio: 0)



Utiliza Nuestro Buscador

Deja una respuesta

Tu dirección de correo electrónico no será publicada.