如何结束C ++代码
我想我的C ++代码停止运行,如果满足某些条件,但我不知道如何做到这一点。 所以只要if
语句是真的就可以像这样终止代码:
if (x==1) { kill code; }
有几种方法,但首先你需要理解为什么对象清理是重要的,因此std::exit
在C ++程序员中被边缘化。
RAII和堆栈展开
C ++使用了一个名为RAII的习惯用法,简单的说就是对象应该在构造函数中执行初始化,并在析构函数中进行清理。 例如, std::ofstream
类[可以]在构造函数中打开文件,然后用户对其执行输出操作,最后在其生命周期结束时(通常由其范围确定),析构函数被调用,基本上closures该文件并将任何写入的内容刷新到磁盘中。
如果你没有到达析构函数来刷新和closures文件,会发生什么? 谁知道! 但可能它不会写入所有应该写入文件的数据。
例如考虑这个代码
#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
的生命周期的结束,因此调用它的析构函数并通过closures和清理文件到磁盘来进行适当的清理。 - 可能性2:抛出一个exception也会照顾当前范围内的对象的生命周期,从而做适当的清理。
- 可能性3:在这里堆栈放卷进入行动! 即使exception是在
inner_mad
引发的,开卷器会通过mad
和main
堆栈来执行适当的清理,所有的对象都会被正确地破坏,包括ptr
和os
。 - 可能性4:那么,在这里?
exit
是一个C函数,它不知道也不兼容C ++习惯用法。 它不会对你的对象执行清理,包括在同一个范围内的os
。 因此,您的文件将不会正确closures,因此内容可能永远不会被写入! - 其他可能性:通过执行一个隐含的
return 0
,它就会离开主要的作用域,从而和可能性1有相同的效果,也就是正确的清理。
但是不要对我刚刚告诉你的事情有所了解(主要是可能性2和3); 继续阅读,我们会发现如何执行一个适当的exception清理。
可能的方法结束
从主返回!
你应该尽可能地做到这一点。 总是喜欢从你的程序返回从主返回一个适当的退出状态。
你的程序的调用者,也可能是操作系统,可能想知道你的程序应该做的是否成功。 出于同样的原因,您应该返回零或EXIT_SUCCESS
来表示程序成功终止, EXIT_FAILURE
表示程序终止失败,任何其他forms的返回值都是实现定义的( §18.5/ 8 )。
然而,你可能会在调用堆栈中很深,并且返回所有这些可能是痛苦的…
[不要]抛出exception
抛出exception将使用堆栈展开来执行适当的对象清理,方法是调用任何先前范围内的每个对象的析构函数。
但是,这是抓住 ! 当处理抛出的exception(catch(…)子句)时,或者即使在调用堆栈中间有一个noexcept
函数时,是否执行堆栈展开。 这在§15.5.1中有规定[except.terminate] :
在某些情况下,必须放弃exception处理,以减less细微的error handling技术。 [注:这些情况是:
[…]
– 当exception处理机制找不到抛出exception的处理程序(15.3)时,或者当处理程序(15.3)的search遇到具有不允许exception(15.4) 的
noexcept
的最外层块时 ,要么 […][…]
在这种情况下,调用std :: terminate()(18.8.3)。 在没有find匹配处理程序的情况下,在std :: terminate()被调用之前,是否实现定义了是否解开堆栈 […]
所以我们必须抓住它!
抛出一个exception,并抓住主!
由于未捕获的exception可能不会执行堆栈展开(因此不会执行正确的清理) ,因此我们应该在main中捕获exception,然后返回退出状态( 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
这不会执行任何types的堆栈展开,堆栈上的活动对象将不会调用其各自的析构函数来执行清理。
这在§3.6.1/ 4 [basic.start.init]中被强制执行:
在不离开当前块的情况下终止程序(例如,通过调用函数std :: exit(int)(18.5))不会销毁具有自动存储持续时间(12.4)的任何对象 。 如果std :: exit在销毁具有静态或线程存储持续时间的对象期间被调用来结束程序,则程序具有未定义的行为。
现在想一想,你为什么要这样做? 你痛苦地损坏了多less物体?
其他[作为坏]的select
还有其他的方式来终止一个程序(除了崩溃) ,但是他们不被推荐。 为了澄清起见,他们将在这里介绍。 注意正常程序的终止 并不意味着堆栈展开,而是操作系统的一个好的状态。
-
std::_Exit
导致正常的程序终止,就是这样。 -
std::quick_exit
导致正常的程序终止,并调用std::at_quick_exit
处理程序,不执行其他清理。 -
std::exit
导致正常的程序终止,然后调用std::atexit
处理程序。 执行其他types的清理,例如调用静态对象析构函数。 -
std::abort
导致程序exception终止,不执行清理。 如果程序以真正意想不到的方式终止,应该调用它。 它什么也不做,只是向操作系统发出exception终止的信号。 在这种情况下,一些系统执行核心转储。 -
std::terminate
调用默认情况下调用std::abort
的std::terminate_handler
。
正如马丁·约克所说的,退出并不像返回那样进行必要的清理。
在退出的地方使用退货总是比较好的。 如果你不是主要的,无论你想退出程序,首先返回到主。
考虑下面的例子。 通过以下程序,将会创build一个文件,其中提到的内容。 但是,如果返回值是注释和未注释的exit(0),编译器不会保证文件将具有所需的文本。
int main() { ofstream os("out.txt"); os << "Hello, Can you see me!\n"; return(0); //exit(0); }
不仅如此,在一个程序中有多个退出点将使debugging更加困难。 只有在可以certificate的情况下才使用退出。
调用std::exit
函数。
人们说“呼叫退出(返回码)”,但这是不好的forms。 在小程序中很好,但是有一些问题:
- 您将最终从程序中获得多个退出点
- 它使代码更复杂(如使用goto)
- 它不能释放在运行时分配的内存
真的,唯一一次你应该退出这个问题的是main.cpp中的这一行:
return 0;
如果你使用exit()来处理错误,你应该学习exception(和嵌套exception),作为一个更优雅和安全的方法。
return 0;
把它放在int main()
,然后程序立即closures。
要么从您的main
返回一个值或使用exit
function。 两者都是一个整数。 除非您有一个外部stream程监视返回值,否则返回的值并不重要。
执行stream程到达主函数结束时,程序将终止。
要在此之前终止它,可以使用exit(int status)函数,其中状态是返回到程序启动的任何值。 0通常表示一个非错误状态
如果你在代码的某个地方有一个错误,那么要么抛出一个exception,要么设置错误代码。 抛出exception而不是设置错误代码总是更好。
通常你可以使用exit()
方法和适当的退出状态 。
零将意味着成功的运行。 非零状态表示出现了某种问题。 父进程(例如shell脚本)使用此退出码来确定进程是否已成功运行。
除了调用exit(error_code) – 调用atexit处理程序,而不是RAII析构函数等 – 越来越多的我使用exception。
越来越多的我的主程序看起来像
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); } }
在那里secondary_main所有原来的东西放在哪里 – 即原来的主被重命名为secondary_main,并添加了上面的存根主。 这只是一个很好的,所以在托盘和主要的捕获之间没有太多的代码。
如果你想要,捕获其他exceptiontypes。
我很喜欢捕捉string错误types,如std :: string或char *,并打印在捕获处理程序在主。
使用这样的exception至less允许调用RAII析构函数,以便它们可以进行清理。 这可能是愉快和有用的。
总体而言,Cerror handling – 退出和信号 – 和C ++error handling – 尝试/捕获/抛出exception – 最好不一致。
那么,你在哪里发现一个错误
throw "error message"
或者一些更具体的exceptiontypes。
打破条件使用返回(0);
所以,你的情况是这样的:
if(x==1) { return 0; }
如果你的if语句是循环的,你可以使用
break;
如果你想逃避一些代码并继续循环使用:
继续;
如果你的if语句不在循环中你可以使用:
return 0; Or exit();
Dude … exit()
函数在stdlib.h下定义
所以你需要添加一个预处理器。
把标题部分include stdlib.h
然后使用exit();
无论你喜欢什么,但要记住在退出的括号中加一个整数。
例如:
exit(0);
如果我testing的条件真的是坏消息,我这样做:
*(int*) NULL= 0;
这给了我一个很好的核心从我可以检查的情况。