我想我的 C ++ 代码停止运行,如果满足一定的条件,但我不知道如何做到这一点。所以在任何时候,如果if
语句是真的终止代码是这样的:
if (x==1)
{
kill code;
}
有几种方法,但首先你需要了解为什么对象清理很重要,因此std::exit
在 C ++ 程序员中被边缘化的原因。
RAII 和堆栈展开
C ++ 使用了一个名为RAII的成语,简单地说,这意味着对象应该在构造函数中执行初始化并在析构函数中执行清理。例如,std::ofstream
类 [可能] 在构造函数期间打开文件,然后用户对其执行输出操作,最后在其生命周期结束时(通常由其范围决定),调用析构函数,该析构函数基本上关闭文件并刷新任何磁盘。
如果您没有到析构函数刷新并关闭文件,会发生什么?谁知道!但可能它不会写入它应该写入文件的所有数据。
例如,考虑这个代码
#include <fstream>
#include <exception>
#include <memory>
void inner_mad()
{
throw std::exception();
}
void mad()
{
auto ptr = std::make_unique<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 回来!
你应该尽可能这样做;总是喜欢从程序返回,从 main 返回一个正确的退出状态。
您的程序的调用者,可能还有操作系统,可能想知道您的程序应该做的是否成功。出于同样的原因,您应该返回零或EXIT_SUCCESS
以指示程序成功终止,EXIT_FAILURE
以指示程序未成功终止,任何其他形式的返回值都是实现定义的(§ 18.5 / 8)。
然而,你可能在调用堆栈很深,并返回所有可能是痛苦的...
[不] 抛出异常
抛出异常将通过调用任何先前范围中每个对象的析构函数,使用堆栈展开执行正确的对象清理。
但是这里是catch!它是实现定义的,当抛出的异常未被处理时,是否执行堆栈展开(由 catch (...) 子句),或者即使您在调用堆栈的中间有一个noexcept
函数。这在§ 15.5.1 [except.terminate]中有说明:
在某些情况下,必须放弃异常处理,以获得不太微妙的错误处理技术。[注意:这些情况是:
[...]
-当异常处理机制找不到抛出的异常的处理程序时 (15.3),或者当搜索处理程序 (15.3) 遇到不允许异常的noexcept
-规范的函数的最外层块时 (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;
}
/* ... */
}
[不要] 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
导致程序异常终止,不执行清理。如果程序以非常非常意外的方式终止,则应调用此方法。它只会向操作系统发送有关异常终止的信号。在这种情况下,某些系统会执行核心转储。
正如马丁 · 约克(Martin York)提到的那样,出口不会像退货那样进行必要的清理。
在退出的地方使用 return 总是更好的。如果你不在 main 中,无论你想退出程序,首先返回 main。
考虑下面的例子。使用下面的程序,将创建一个包含所提到内容的文件。但是如果 return 是 commented & amp;uncommented exit (0),编译器不会保证该文件将具有所需的文本。
int main()
{
ofstream os("out.txt");
os << "Hello, Can you see me!\n";
return(0);
//exit(0);
}
不仅如此,在程序中具有多个退出点会使调试更加困难。仅在合理的情况下使用 exit。
调用std::exit
函数。
人们说“调用退出(返回代码)”,但这是不好的形式。在小程序中很好,但是有很多问题:
您最终将从程序中获得多个退出点
它使代码更复杂(如使用 goto)
它不能释放在运行时分配的内存
真的,你应该退出问题的唯一时间是在 main.cpp 中的这一行:
return 0;
如果您使用 exit()处理错误,您应该了解异常(和嵌套异常),作为一个更优雅和安全的方法。
本站系公益性非盈利分享网址,本文来自用户投稿,不代表边看边学立场,如若转载,请注明出处
评论列表(79条)