如果满足某个条件,我希望我的C++代码停止运行,但我不知道该怎么做.因此,只要if
声明为真,就可以在任何时候终止代码,如下所示:
if (x==1)
{
kill code;
}
Run Code Online (Sandbox Code Playgroud)
Den*_*rim 417
有几种方法,但首先你需要理解为什么对象清理很重要,因此std::exit
在C++程序员中边缘化的原因.
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);
}
Run Code Online (Sandbox Code Playgroud)
每种可能性都会发生什么:
os
因此调用它的析构函数并通过关闭并将文件刷新到磁盘来进行适当的清理.inner_mad
,展开器也将通过堆栈mad
并main
进行适当的清理,所有对象都将被正确破坏,包括ptr
和os
.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中捕获异常然后返回退出状态(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;
}
/* ... */
}
Run Code Online (Sandbox Code Playgroud)
这不执行任何类型的堆栈展开,并且堆栈上没有活动对象将调用其各自的析构函数来执行清理.
这在§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
.Nar*_*a N 61
正如马丁约克所提到的那样,退出并不像返回那样进行必要的清理.
在出口处使用返回总是更好.如果您不在主要位置,无论您想要退出该计划,请先返回主要位置.
考虑下面的例子.使用以下程序,将使用提及的内容创建文件.但是如果返回被注释并且未注释exit(0),则编译器不会向您保证该文件将具有所需的文本.
int main()
{
ofstream os("out.txt");
os << "Hello, Can you see me!\n";
return(0);
//exit(0);
}
Run Code Online (Sandbox Code Playgroud)
不仅如此,在程序中拥有多个退出点将使调试更加困难.只有在可以对齐时才使用exit.
jke*_*eys 23
人们说"呼叫退出(返回代码)",但这是不好的形式.在小程序中它很好,但是有很多问题:
真的,唯一一次你应该退出问题的是main.cpp中的这一行:
return 0;
Run Code Online (Sandbox Code Playgroud)
如果您使用exit()来处理错误,您应该了解异常(和嵌套异常),作为一种更优雅和安全的方法.
Eva*_*ake 14
return 0;
把它放在你想要的任何地方int main()
,程序将立即关闭.
Goz*_*Goz 11
从您的返回值main
或使用该exit
函数.两者都采取int.除非您有外部流程监视返回值,否则返回的值并不重要.
chr*_*ney 11
程序将在执行流程到达主函数结束时终止.
要在此之前终止它,可以使用exit(int status)函数,其中status是返回到启动程序的任何值的值.0通常表示非错误状态
Jag*_*ath 11
如果代码中某处有错误,则抛出异常或设置错误代码.抛出异常而不是设置错误代码总是更好.
除了调用exit(error_code) - 它调用atexit处理程序,而不调用RAII析构函数等 - 越来越多我使用异常.
越来越多我的主程序看起来像
int main(int argc, char** argv)
{
try {
exit( secondary_main(argc, argv );
}
catch(...) {
// optionally, print something like "unexpected or unknown exception caught by main"
exit(1);
}
}
Run Code Online (Sandbox Code Playgroud)
其中secondary_main所在的所有东西都放在哪里 - 即原始main重命名为secondary_main,并添加上面的stub main.这只是一个非常好的,所以托盘和主要的捕获之间没有太多的代码.
如果需要,请捕获其他异常类型.
我非常喜欢捕获字符串错误类型,比如std :: string或char*,并在main中的catch处理程序中打印它们.
使用这样的异常至少允许调用RAII析构函数,以便它们可以进行清理.这可能是愉快和有用的.
总的来说,C错误处理 - 退出和信号 - 以及C++错误处理 - 尝试/捕获/抛出异常 - 最好不一致地一起玩.
然后,您在哪里检测到错误
throw "error message"
Run Code Online (Sandbox Code Playgroud)
或一些更具体的异常类型.