提前退出程序
本节阅读量:本节讨论的控制流语句是退出程序,这也是本章讨论的最后一类控制流语句。退出表示程序终止执行。在C++中,它通过函数(而不是关键字)实现,因此退出程序需要调用函数。
先绕一个小弯,回顾一下程序正常退出时会发生什么。当main()函数返回时(到达函数末尾或通过return语句返回),会发生许多事情。
首先,程序会离开main函数,因此所有局部变量和函数参数都会被销毁(通常情况下)。
接下来,调用一个名为std::exit()的特殊函数,并将main()函数的返回值作为参数传入。那么什么是std::exit()?
std::exit()函数
exit()是一个导致程序正常终止的函数。正常终止意味着程序以预期的方式退出。请注意,术语正常终止并不意味着程序执行符合预期。例如,假设您正在编写一个程序,希望用户输入要处理的文件名。如果用户键入了无效的文件名,则程序可能会返回非零的状态码来指示故障状态,但它仍然是正常的终止。
exit()会执行许多清理操作。首先,销毁具有静态存储期的对象。然后,如果使用了文件,则执行一些文件清理动作。最后,将传递给std::exit()的参数用作状态码,并把控制权交还给操作系统。
显式调用std::exit()
尽管在函数main()结束时隐式调用std::exit(),但也可以显式调用std::exit。当以这种方式调用std::exit()时,需要引用cstdlib头文件。
下面是显式使用std::exit()的示例:
|
|
该程序打印:
|
|
请注意,std::exit()后续的语句永远不会执行,因为程序已经终止。
在上面的程序中,从函数main()中调用std::exit(),但可以从任何函数中调用std::exit()来终止程序。
显式调用std::exit()不会清理任何局部变量(包括当前函数中,以及调用堆栈上其他函数中的局部变量)。因此,通常最好避免调用std::exit()。
警告
std::exit()函数不清理当前函数以及调用堆栈中的局部变量。
std::atexit
由于std::exit()立即终止程序,因此您可能希望在终止之前手动进行一些清理。在这种情况下,清理意味着关闭数据库或网络连接、释放分配的任何内存、将信息写入日志文件等…
上面的示例调用cleanup()函数来处理清理任务。然而,在每次调用exit()之前手动调用清理函数,会给我们增加很多负担。
为了帮助实现这一点,C++提供了std::atexit()函数,它允许您指定一个函数,该函数将在程序终止时通过std::exit()自动调用。
下面是一个示例:
|
|
该程序的输出与前面的示例相同:
|
|
为什么要这么做?这允许我们在一个地方(可能在main中)指定清理函数,之后就不必担心在调用std::exit()之前忘记显式调用清理函数。
这里有几点关于std::atexit()和清理函数的注意事项:首先,由于main()终止时会隐式调用std::exit(),因此如果程序以这种方式退出,也会调用std::atexit()注册的所有函数。其次,被注册的函数必须没有参数,并且没有返回值。最后,如果需要,可以使用std::atexit()注册多个清理函数,它们将按注册顺序的相反顺序调用(最后注册的函数会最先调用)。
对于高级读者
在多线程程序中,调用std::exit()可能会导致程序崩溃(因为调用std::exit()的线程会清理静态对象,而这些对象可能仍被其他线程访问)。因此,C++引入了另一对工作方式类似于std::exit()和std::atexit()的函数,称为std::quick_exit()和std::at_quick_exit()。std::quick_exit()会终止程序,但不会清理静态对象,也不一定执行其他类型的清理。对于以std::quick_exit()终止的程序,std::at_quick_exit()扮演的角色与std::atexit()相同。
std::abort和std::terminate
C++包含另外两个与退出程序相关的函数。
std::abort()导致程序异常终止。异常终止意味着程序出现某种异常运行时错误,程序无法继续运行。例如,尝试除以0将导致异常终止。std::abort()不执行任何清理。
|
|
在本章后面的部分中,我们将看到隐式调用std::abort的情况(9.6–Assert和static_Assert)。
std::terminate()函数通常与异常一起使用(我们将在后面的一章中介绍异常)。尽管可以显式调用std::terminate,但在处理异常时(以及在其他一些与异常相关的情况下),它经常被隐式调用。默认情况下,std::terminate()会调用std::abort()。
什么时候需要手动退出程序?
简短的回答是“几乎从不”。销毁局部对象是C++的一个重要部分(特别是在使用class时),而上面提到的函数都不能清理局部变量。
最佳实践
只有在没有安全的方法从main函数正常返回时才手动退出程序。
提示
尽管应该尽量减少手动退出程序,但程序仍可能通过许多其他方式意外关闭。例如:
- 应用程序可能由于错误而崩溃(在这种情况下,操作系统将关闭它)。
- 用户可能会以各种方式终止应用程序。
- 用户可能会关闭其计算机的电源。
- 太阳可能会变成超新星,并在一个巨大的火球中吞噬地球。
一个设计良好的程序应该能够在任何时候被关闭,并尽量降低影响。
一个常见例子是,现代游戏通常会定期自动保存游戏状态和用户设置。这样,如果游戏意外关闭且用户没有手动保存,用户之后仍可以通过先前的自动保存继续游戏,而不会损失太多进度。