转到或扔,优点/缺点
goto or throw, advantages/disadvantages
我有这段代码:
try
{
// ...do something... possibly goto error
}
catch (...)
{
}
error:
// ...process error...
我面临的问题是我是否应该使用goto
(如果可能)或throw
跳转到error
标签。这两种方法的(缺点)优点是什么?
编辑:修复代码以符合标准。
编辑:现在这是一个完全不同的问题,答案也完全不同。
我相信,将其替换为错误处理的最惯用模式是使用自定义删除器将 C 库提供给您的内容std::unique_ptr
或std::shared_ptr
地提供给 RAII,在这种情况下,正常情况下的特殊处理将不再需要它们。然后,您可以按常规方式处理错误:
// just for a simple example.
std::unique_ptr<void, void(*)(void*)> c_obj(malloc(1000), free);
try {
// Don't use goto, throw.
} catch(...) {
// handle error here
}
// label not required anymore, cleanup handled by custom deleters. That means
// that free will be called when c_obj is destroyed.
我强调这是为了错误处理,因为将throw
用于正常的控制流是不好的,原因与goto
不好的原因相同:代码的结构越像一团意大利面条,就越难以理解和维护。如果这是正常的控制流,我会告诉你寻找以更有条理的方式重述问题的方法。
对原始问题的回答
在这种情况下使用goto
格式不正确,如[除了](C++11中的15(3)C++03中的15(2)
不得使用
goto
或switch
语句将控制权转移到 try 块或处理程序中。[示例:
void f() {
goto l1; // Ill-formed
goto l2; // Ill-formed
try {
goto l1; // OK
goto l2; // Ill-formed
l1: ;
} catch(...) {
l2: ;
goto l1; // Ill-formed
goto l2; // OK
}
}
-- 结束示例] (...
因此,您的编译器完全拒绝此类代码是合理的,实际上 gcc 和 clang 都拒绝编译它。
我怀疑它被禁止的原因与禁止跳过带有初始化的声明的原因相同,即在没有异常的情况下在异常处理程序中并不比在没有变量的变量范围内更有意义。就像具有自动存储持续时间的变量在作用域结束时处理(调用析构函数)一样,异常也可以在异常处理程序1 结束时处理。如果两者都不存在,那将是一个问题。
当然,如果您尝试自己使用它们,那也将是一个问题,这可能是禁止使用它们的另一部分原因。
1将是,除非您说 throw;
,在这种情况下,异常将在处理程序中处理。
撇开所有关于你的想法合法性的担忧[1],忽略从C++11开始使用std::current_exception
的可能性[2],你没有办法从包罗万象的条款中以有意义的方式处理异常......
。除非你重新抛出或者你根本不关心发生了什么样的异常。但是,在不知道发生了什么异常的情况下,除了杀死进程之外,您还会做什么。
首先处理异常的要点是,处理不仅仅是崩溃和刻录(或者你可以让编译器调用terminate
,这更容易,代码更少!但处理需要以某种方式有意义。
这就是为什么throw;
例如在这个常见的成语中使用的goto
远远优于:
void handler()
{
try { throw; } catch (foo& f){} catch(bar& b){} /* ... */
}
// ...
try{ /* ... */ }
catch(...) { handler(); }
是的,goto
本质上并不是邪恶的,但人们应该只在极少数情况下使用它,它实际上使代码更好、更简洁、更易读。这不是这样一种情况。
你可能会争辩说重新抛出是昂贵的,但这不是一个有效的论点。异常发生异常,但一旦遇到异常,性能就不再重要了。
[1]很确定它不符合标准,虽然我相信GCC无论如何都会让你这样做,但goto
上有一些奇怪的扩展可以让你做有趣的事情。
[2]虽然它对于将异常传递给另一个线程可能很有用,但与"适当的"解决方案相比,它非常丑陋和笨拙。
关于goto
的简单规则(几乎总是):假设您不知道使用 goto
的具体原因,那么不要。基本上,如果您无法向其他人解释"我在这里使用goto
是因为......"(以及"因为..."是一个有效且很好的理由,而不仅仅是"我是一个懒惰的程序员,如果 if/loop/etc 懒得添加另一个级别"[这是我时不时地的借口!
。 throw
将有助于清理,而goto
不会,因此在中间范围内需要清理的任何局部变量都不会
使用 throw
还允许您使用一个throw
摆脱多个级别的函数调用(换句话说,您可以在一个相当深的调用堆栈中,并一直到更远的某个级别catch
)。
正如 Wintermute 指出的那样,它也不是有效的C++代码,因此即使您有很好的理由这样做,它也可能"按预期工作"。然后,您必须在捕获之前执行一个转到,然后在那里进行投掷以进入捕获块。
- 函数局部静态变量:从性能角度来看的优点/缺点
- 优点和缺点 在类内为大型项目定义的内联朋友助手免费函数
- 在工厂中使用静态方法:优点和缺点?
- 在将GITHUB库包含在您的项目中之前,汇编GitHub库的优点 /缺点是什么?
- 优点和缺点..作为值或引用的常量
- 在非GUI应用程序,优点和缺点中使用QT
- 使用 unique_ptr 作为方法参数 - 优点和缺点
- 在Java中使用本机代码的优点和缺点
- 使用 std::stack 而不仅仅是 deque、vector 或 list 有什么优点和缺点
- 转到或扔,优点/缺点
- 使用指针的优点/缺点
- 以不同方式实现可变参数构造函数的模板类:每个版本的优点和缺点是什么
- 在 C++ pimp 中使用空指针的优点和缺点
- 集中式事件调度的优点和缺点
- 在名称空间中组织变量和方法的优点和缺点
- VM解释器-更大指令集/调度循环的加权性能优点和缺点
- 作为类成员持有对外部对象的引用的优点/缺点
- 使用类的方法而不实例化的优点/缺点
- VS2008传递变量-结构与结构组件-优点/缺点
- 继承、组合和多成员变量的优点/缺点