如何结束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引发的,开卷器会通过madmain堆栈来执行适当的清理,所有的对象都会被正确地破坏,包括ptros
  • 可能性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]

  1. 在某些情况下,必须放弃exception处理,以减less细微的error handling技术。 [注:这些情况是:

    […]

    当exception处理机制找不到抛出exception的处理程序(15.3)时,或者当处理程序(15.3)的search遇到具有不允许exception(15.4) noexcept的最外层块时 ,要么 […]

    […]

  2. 在这种情况下,调用std :: terminate()(18.8.3)。 在没有find匹配处理程序的情况下,在std :: terminate()被调用之前,是否实现定义了是否解开堆栈 […]

所以我们必须抓住它!

抛出一个exception,并抓住主!

由于未捕获的exception可能不会执行堆栈展开(因此不会执行正确的清理) ,因此我们应该在main中捕获exception,然后返回退出状态( EXIT_SUCCESSEXIT_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::abortstd::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。 在小程序中很好,但是有一些问题:

  1. 您将最终从程序中获得多个退出点
  2. 它使代码更复杂(如使用goto)
  3. 它不能释放在运行时分配的内存

真的,唯一一次你应该退出这个问题的是main.cpp中的这一行:

 return 0; 

如果你使用exit()来处理错误,你应该学习exception(和嵌套exception),作为一个更优雅和安全的方法。

return 0; 把它放在int main() ,然后程序立即closures。

要么从您的main返回一个值或使用exitfunction。 两者都是一个整数。 除非您有一个外部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; 

这给了我一个很好的核心从我可以检查的情况。