2012-02-03 20 views
17

Estoy aprendiendo sobre el idioma RAII en C++, y cómo usar punteros inteligentes.¿Cómo funciona RAII cuando un constructor lanza una excepción?

En mi lectura, me he encontrado con dos cosas que, para mí, parecen contradecirse entre sí.

Citado de http://www.hackcraft.net/raii/:

... si un objeto miembro con la semántica RAII ha sido creado y una excepción que ocurre antes de que el constructor ha completado entonces su destructor será llamado como parte de la pila de desenrollado. Por lo tanto, un objeto que controle múltiples recursos puede garantizar su limpieza incluso si no está completamente construido mediante el uso de objetos RAII miembros.

Pero citado de http://www.parashift.com/c++-faq-lite/exceptions.html#faq-17.10:

Si un constructor lanza una excepción, destructor del objeto no se ejecuta. Si su objeto ya ha hecho algo que debe deshacerse (como asignar algo de memoria, abrir un archivo o bloquear un semáforo), este "elemento que debe deshacerse" debe ser recordado por un miembro de datos dentro del objeto.

Y luego, la segunda fuente vinculada recomienda el uso de punteros inteligentes para tratar el tema de las cosas que ya estaban asignadas en el constructor.

Entonces, ¿qué sucede realmente en estos escenarios?

+8

+1 esta es la forma en que "newprogrammer [s]" * debería * hacer preguntas! –

Respuesta

11

No entiende bien la primera cita. Eso no es difícil, ya que es confuso.

si se ha creado un objeto miembro con semántica RAII y ocurre una excepción antes de que el constructor haya completado, se llamará a su destructor como parte del desenrollado de la pila.

Eso es lo que dice. Esto es lo que significaba:

si se ha creado un objeto miembro con la semántica RAII y una excepción ocurre en el objeto externo antes constructor de la del objeto externo ha completado entonces destructor del objeto miembro se ser llamado como parte del desenrollado de la pila.

Ver la diferencia? La idea es que el objeto miembro completó su constructor, pero el tipo propietario no.Tiró en algún lugar de su constructor (o un constructor de otro miembro que se inicializó después de ese). Esto hará que se llame al destructor de todos sus miembros (todos los que completaron la construcción, es decir), pero no a su propio destructor.

He aquí un ejemplo:

class SomeType 
{ 
    InnerType val; 
public: 
    SomeType() : val(...) 
    { 
    throw Exception; 
    } 
}; 

Cuando se crea una instancia de SomeType, se llamará InnerType::InnerType. Siempre que eso no arroje, ingresará al constructor SomeType. Cuando eso arroja, causará la destrucción de val, llamando al InnerType::~InnerType.

+1

Ah, eso tiene sentido ... Por cierto, ¿eres tú el que escribió la maravillosa guía gráfica en 3D? No puedo agradecerles lo grande que es esa guía. – newprogrammer

+3

"Esto hará que se llame al destructor de todos sus miembros, pero no a su propio destructor". - Bueno, solo los que completaron la construcción antes de que se lanzara la excepción ... –

2

Estas dos declaraciones no se contradicen entre sí, pero la primera tiene un lenguaje desafortunado. Cuando se lanza la construcción de algún objeto, no se llamará al deconstructor, pero todos sus objetos serán destruidos por sus deconstructores individuales.

Por lo tanto, con RAII y punteros inteligentes, los destructores para cualquier miembro del puntero de un objeto se invocarán independientemente del destructor del objeto que se debe. Los punteros crudos no liberan la memoria a la que apuntan y deben borrarse manualmente. Si el constructor del objeto propietario arroja punteros sin formato, no se liberará. Esto no puede suceder con punteros inteligentes.

5

No hay contradicción aquí; solo se usa una terminología confusa en diferentes contextos.

Si el constructor de un objeto se produce una excepción, entonces ocurre lo siguiente (suponiendo que se detecta la excepción):

  1. Todas las variables locales en el constructor han invocado sus destructores, la liberación de todos los recursos que han adquirido (si alguna).
  2. Todos los subobjetos directos del objeto cuyo constructor arrojó una excepción invocarán sus destructores, liberando recursos que han adquirido (si los hay).
  3. Todas las clases base del objeto cuyo constructor lanzó tendrán sus destructores invocados (ya que fueron construidos por completo antes de que se ejecutara el constructor de clases derivado)
  4. Se realizará una limpieza adicional de la persona que llama, etc.

Como resultado, todos los recursos que son administrados por punteros inteligentes u otros objetos RAII que son los datos de los miembros del objeto que se está destructed de hecho va a ser limpiado, pero el código especializado para hacer la limpieza en el destructor del objeto ganaron disparar

Espero que esto ayude!

+0

¡Sí, su descripción paso a paso lo resolvió todo! – newprogrammer

Cuestiones relacionadas