3 回答
TA贡献1828条经验 获得超13个赞
有几种方法,但首先你需要理解为什么对象清理很重要,因此std::exit
在C ++程序员中边缘化的原因。
RAII和堆栈展开
C ++使用了一个名为RAII的习语,简单来说就是对象应该在构造函数中执行初始化并在析构函数中进行清理。例如,std::ofstream
类[可]在构造函数中打开文件,然后用户对其执行输出操作,最后在其生命周期结束时(通常由其作用域确定),调用析构函数,实质上关闭文件并刷新任何书面内容进入磁盘。
如果你没有到析构函数刷新并关闭文件会发生什么?谁知道!但可能它不会写入它应该写入文件的所有数据。
例如,考虑这段代码
#include <fstream>#include <exception>#include <memory>void inner_mad(){ throw std::exception();}void mad(){ std::unique_ptr<int> ptr(new int); inner_mad();}int main(){ std::ofstream os("file.txt"); os << "Content!!!"; int possibility = /* either 1, 2, 3 or 4 */; if(possibility == 1) return 0; else if(possibility == 2) throw std::exception(); else if(possibility == 3) mad(); else if(possibility == 4) exit(0);}
每种可能性都会发生什么:
可能性1:返回基本上离开了当前的函数范围,因此它知道生命周期的结束
os
因此调用它的析构函数并通过关闭并将文件刷新到磁盘来进行适当的清理。可能性2:抛出异常也会处理当前范围内对象的生命周期,从而进行适当的清理......
可能性3:这里堆叠展开进入行动!即使抛出异常
inner_mad
,展开器也将通过堆栈mad
并main
进行适当的清理,所有对象都将被正确破坏,包括ptr
和os
。可能性4:嗯,这里?
exit
是一个C函数,它不知道也不兼容C ++习语。它不会对您的对象执行清理,包括os
在相同的范围内。因此,您的文件将无法正常关闭,因此内容可能永远不会写入其中!其他可能性:它只是通过执行隐式
return 0
而离开主范围,因此具有与可能性1相同的效果,即适当的清理。
但是不要对我刚才告诉你的事情这么肯定(主要是可能性2和3); 继续阅读,我们将找到如何执行适当的异常清理。
可能的方法结束
从主要回来!
你应该尽可能地这样做; 总是喜欢通过从main返回正确的退出状态来从程序返回。
您的程序的调用者,可能还有操作系统,可能想知道您的程序应该执行的操作是否成功。出于同样的原因,您应该返回零或EXIT_SUCCESS
表示程序成功终止并EXIT_FAILURE
发出程序终止失败的信号,任何其他形式的返回值都是实现定义的(第18.5 / 8节)。
但是你可能在调用堆栈中非常深,并且返回所有这些可能会很痛苦......
[不]抛出异常
抛出异常将使用堆栈展开执行正确的对象清理,方法是调用任何先前作用域中每个对象的析构函数。
但这是抓住了!它是实现 - 定义是否在未处理抛出异常时(通过catch(...)子句)执行堆栈展开,或者即使noexcept
在调用堆栈中间有函数也是如此。这在§15.5.1[except.terminate]中说明:
在某些情况下,必须放弃异常处理以获得不太精细的错误处理技术。[注意:这些情况是:
[...]
- 当异常处理机制找不到抛出异常的处理程序时(15.3),或者当搜索处理程序(15.3)遇到函数的最外层块时,
noexcept
-specification不允许异常(15.4),或者[...][...]
在这种情况下,调用std :: terminate()(18.8.3)。在没有找到匹配处理程序的情况下,无论是否在调用std :: terminate()之前展开堆栈,它都是实现定义的 [...]
所以我们必须抓住它!
抛出一个异常并在main处抓住它!
由于未捕获的异常可能不执行堆栈展开(因此不会执行正确的清理),我们应该在main中捕获异常然后返回退出状态(EXIT_SUCCESS
或EXIT_FAILURE
)。
所以一个可能很好的设置将是:
int main(){ /* ... */ try { // Insert code that will return by throwing a exception. } catch(const std::exception&) // Consider using a custom exception type for intentional { // throws. A good idea might be a `return_exception`. return EXIT_FAILURE; } /* ... */}
[不] std :: exit
这不执行任何类型的堆栈展开,并且堆栈上没有活动对象将调用其各自的析构函数来执行清理。
这在§3.6.1/ 4 [basic.start.init]中强制执行:
在不离开当前块的情况下终止程序(例如,通过调用函数std :: exit(int)(18.5))不会销毁具有自动存储持续时间的任何对象(12.4)。如果在销毁具有静态或线程存储持续时间的对象期间调用std :: exit来结束程序,则程序具有未定义的行为。
现在想一想,你为什么要做这样的事情?你痛苦地损坏了多少件物品?
其他[作为坏]替代品
还有其他方法可以终止程序(崩溃除外),但不推荐使用它们。只是为了澄清它们将在这里展示。注意正常的程序终止 并不意味着堆栈展开,而是指操作系统的正常状态。
std::_Exit
导致正常的程序终止,就是这样。std::quick_exit
导致正常的程序终止并调用std::at_quick_exit
处理程序,不执行其他清理。std::exit
导致正常的程序终止,然后调用std::atexit
处理程序。执行其他类型的清理,例如调用静态对象析构函数。std::abort
导致程序异常终止,不执行清理。如果程序以非常非常意外的方式终止,则应调用此方法。除了向操作系统发出异常终止信号外,它什么都不做。在这种情况下,某些系统会执行核心转储。std::terminate
默认情况下调用std::terminate_handler
哪些调用std::abort
。
TA贡献1862条经验 获得超7个赞
如果你的if语句在循环中你可以使用
break;
如果你想逃避一些代码并继续循环使用:
继续;
如果你的if语句不在循环中你可以使用:
return 0;Or exit();
- 3 回答
- 0 关注
- 655 浏览
添加回答
举报