c++ - ¿Por qué `std:: exit` no activa los destructores como se esperaba?
c++11 global-variables (2)
El problema aquí es que al salir del proceso, el hilo será (en la mayoría de los sistemas operativos multitarea modernos) matado por la fuerza. Esta muerte del hilo ocurre a nivel del sistema operativo, y el sistema operativo no sabe nada acerca de los objetos o destructores.
#include <cstdlib>
#include <thread>
#include <chrono>
#include <iostream>
using namespace std;
using namespace std::literals;
struct A
{
int n_ = 0;
A(int n) : n_(n) { cout << "A:" << n_ << endl; }
~A() { cout << "~A:" << n_ << endl; }
};
A a1(1);
int main()
{
std::thread([]()
{
static A a2(2);
thread_local A a3(3);
std::this_thread::sleep_for(24h);
}).detach();
static A a4(4);
thread_local A a5(5);
std::this_thread::sleep_for(1s);
std::exit(0);
}
Mi compilador es el -std=c++1z
clang 5.0
con -std=c++1z
.
La salida es la siguiente:
A:1 A:2 A:4 A:5 A:3 ~A:5 ~A:2 ~A:4 ~A:1
Tenga en cuenta que no hay ~A:3
, lo que significa que el objeto A a3
no se destruyó.
Sin embargo, según cppref :
std::exit
causa la terminación normal del programa. Se realizan varios pasos de limpieza:Los destructores de objetos con duración de almacenamiento local de hilos ... están garantizados para ser llamados.
Los objetos con duración de almacenamiento de subprocesos están garantizados para ser destruidos solo para el subproceso que exit
llamadas. Citando C ++ 14 (N4140), [support.start.term] 18.5 / 8 (énfasis mío):
[[noreturn]] void exit(int status)
La función
exit()
tiene un comportamiento adicional en esta Norma Internacional:
- Primero, se destruyen los objetos con duración de almacenamiento de hilos y asociados con el hilo actual . A continuación, los objetos con duración de almacenamiento estático se destruyen y se llaman las funciones registradas mediante la llamada atexit. Ver 3.6.3 para el orden de destrucciones y llamadas. (Los objetos automáticos no se destruyen como resultado de llamar a
exit()
.) Si el control deja una función registrada llamada porexit
porque la función no proporciona un controlador para una excepción lanzada, se llamará astd::terminate()
(15.5. 1).- A continuación, todas las secuencias C abiertas (como mediadas por las firmas de función declaradas en
<cstdio>
) con datos notmpfile()
se eliminan, todas las secuencias C abiertas se cierran, y se eliminan todos los archivos creados al llamar atmpfile()
.- Finalmente, el control se devuelve al entorno host. Si el estado es cero o
EXIT_SUCCESS
, seEXIT_SUCCESS
una forma definida por la implementación de la terminación exitosa del estado. Si el estado esEXIT_FAILURE
, seEXIT_FAILURE
una forma definida por la implementación de la terminaciónEXIT_FAILURE
del estado. De lo contrario, el estado devuelto está definido por la implementación.
Por lo tanto, el estándar no garantiza la destrucción de objetos con una duración de almacenamiento de subprocesos asociada con otros subprocesos que no sea el que llama a exit
.