resolucion - programacion orientada a objetos c++ ejemplos
¿Se garantiza que un destructor de C++ no se invocará hasta el final del bloque? (8)
El destructor no se invocará hasta que el objeto salga del alcance.
El C ++ faq lite tiene una buena sección sobre dtors
En el código de C ++ a continuación, ¿tengo garantizado que se llamará al destructor ~ obj () después de que // se ejecute más código? ¿O se permite al compilador destruir el objeto obj antes si detecta que no se usa?
{
SomeObject obj;
... // More code
}
Me gustaría utilizar esta técnica para ahorrarme tener que recordar reiniciar una bandera al final del bloque, pero necesito que la bandera permanezca configurada para todo el bloque.
Está bien con esto: es un patrón muy utilizado en la programación en C ++. De la sección estándar C ++ 12.4 / 10, que hace referencia a cuando se llama un destructor:
para un objeto construido con duración de almacenamiento automático cuando el bloque en el que se crea el objeto sale
La destrucción en C ++ es determinista, lo que significa que el compilador no puede mover ese código. (Por supuesto, la optimización puede alinear el destructor, determinar que el código del destructor no interactúa con // More code
y hacer un reordenamiento de instrucciones, pero ese es otro problema)
Si no puede confiar en que se invoque a los destructores cuando se supone que deben ser llamados, no podría usar RAII para obtener bloqueos (o casi cualquier otra construcción RAII para ese asunto):
{
LockClass lock(lockData);
// More code
} // Lock automatically released.
Además, puede depender de destructores que se ejecutan en orden inverso al de cómo se construyeron los objetos.
Sí, el estándar de C ++ tiene requisitos muy específicos sobre cuándo se destruyen los objetos (en §12.4 / 10), y en este caso no debe destruirse hasta que el otro código en el bloque haya terminado de ejecutarse.
Sí, está garantizado
La duración de un objeto con duración de almacenamiento automático finaliza al final de su alcance potencial y no antes. Para dicho objeto, el alcance potencial comienza en el punto de declaración y finaliza al final del bloque en el que se declara. Este es el momento en que se llamará al destructor.
Tenga en cuenta que, hablando muy pedante, incluso para un objeto automático no es correcto decir que se destruye cuando "se sale del alcance" (en lugar de "sale de su alcance potencial "). El objeto puede salir del alcance y regresar al ámbito muchas veces (si se declaran incluso más objetos locales con el mismo nombre dentro del bloque), y salirse del alcance de tal manera no causa la destrucción del objeto. Es el "final final" de su alcance que mata el objeto automático, que se define como el final de su alcance potencial como se describió anteriormente.
De hecho, el estándar de lenguaje ni siquiera se basa en la noción de alcance para describir la vida útil de los objetos automáticos (no es necesario ocuparse de todas estas complejidades terminológicas). Simplemente dice que el objeto se destruye a la salida del bloque en el que está definido :)
Todas las respuestas aquí abordan lo que ocurre con los objetos nombrados, pero, para completar, probablemente también deba conocer la regla para objetos temporales / anónimos. (por ejemplo, f(SomeObjectConstructor()
o f(someFunctionThatReturnsAnObject())
)
Los objetos temporales se destruyen como el último paso en la evaluación de la expresión completa (1.9) que (léxicamente) contiene el punto donde se crearon. Esto es cierto incluso si esa evaluación termina arrojando una excepción. (12.2 / 3 del estándar ISO C ++ 98)
lo que básicamente significa que los objetos generados temporalmente persisten hasta la siguiente declaración. Dos excepciones son para temporales generados como parte de la lista de inicialización de un objeto (en cuyo caso el temporal se destruye solo después de que el objeto está completamente construido) y si se hace referencia a un temporal (ej. const Foo& ref = someFunctionThatReturnsAnobject()
) ( caso la vida del objeto es la duración de la referencia).
Un ejemplo típico de esto, así como su pregunta es boost :: scoped_ptr (o similar std :: auto_ptr):
{
boost::scoped_ptr< MyClass > pMyClass( new MyClass );
// code using pMyClass here
} // destruction of MyClass and memory freed
Actualmente...
C ++ tiene algo llamado el principio "como si". Todas las garantías hechas referidas en todas estas respuestas solo se refieren al comportamiento observable . El compilador puede eludir, reordenar, agregar, etc. cualquier llamada de función, siempre que el comportamiento observable sea como si se hubiera ejecutado como se escribió originalmente. Esto también se aplica a los destructores.
Entonces, técnicamente, su observación es correcta: el compilador puede destruir el objeto antes, si detecta que no se usa, y no hay efectos secundarios observables del destructor o de ninguna función que invoque. Sin embargo, se le garantiza que no podrá ver que esto está sucediendo fuera de un depurador, porque si pudiera saberlo, el compilador ya no podría hacerlo.
Sin embargo, es más probable que el compilador use esta potencia para hacer algo útil, como eludir completamente un destructor trivial en lugar de reordenar las llamadas al destructor.
Editar: Alguien quería una referencia ... 1.9 / 5, junto con la nota al pie 4 del borrador del estándar C ++ 0x (esta no es una regla nueva, simplemente no tengo el estándar C ++ 03 a mano. presente en el estándar C, AFAIK)
1.9 / 5:
Una implementación conforme que ejecuta un programa bien formado debe producir el mismo comportamiento observable que una de las posibles secuencias de ejecución de la instancia correspondiente de la máquina abstracta con el mismo programa y la misma entrada. Sin embargo, si cualquier secuencia de ejecución contiene una operación no definida, esta Norma Internacional no exige que la implementación ejecute ese programa con esa entrada (ni siquiera con respecto a las operaciones anteriores a la primera operación no definida).
Nota al pie 4:
Esta disposición a veces se denomina la regla "como si", porque una implementación es libre de ignorar cualquier requisito de esta norma internacional siempre que el resultado sea como si se hubiera obedecido, en la medida en que pueda determinarse a partir del comportamiento observable. Del programa. Por ejemplo, una implementación real no necesita evaluar parte de una expresión si puede deducir que su valor no se usa y que no se producen efectos secundarios que afecten el comportamiento observable del programa.
Mi lectura (y lo que pensé que era la comprensión general) era que esto es lo que permite al compilador de manos libres hacer lo que quiera (es decir, habilita optimizaciones), siempre y cuando el comportamiento observable sea el de la fuente escrita original, incluido el movimiento alrededor de destructores, no destruyendo objetos en absoluto, inventando destructores, etc.