有时必须执行一系列if / else检查。在过去的日子里,goto是为此而准备的沙漠仪器。
由于goto在许多代码风格指南中是禁止的,我有时会使用循环代替。例如:
do
{
if (a)
{
doA();
break;
}
//...
if (z)
{
doZ();
break;
}
}while(false);
//all break jump here
这是一个好方法吗?是否有一个好的C ++模式(例如使用模板,继承等)来实现它而不需要太多开销?
答案 0 :(得分:7)
由于条件似乎无关,else if
是一个选项:
if (a) {
doA();
} else if (b) {
//...
} else if (z) {
doZ();
}
答案 1 :(得分:5)
通常,对于编写可维护代码,您应该按照预期的方式使用该语言的工具。
你对一个循环进行一次迭代的循环的使用显然是一个偏差,而不是那些首先在goto
上皱眉的同样的代码风格指南。
该语言中提供了一些工具,使您可以更轻松地与其他程序员进行沟通,并让您进一步了解您打算在此处做什么。
return
语句。有趣的早期回报也被一些人所厌恶。答案 2 :(得分:2)
使用goto
,使用continue
,使用多个break
,或使用多个return
,如果被滥用,可以使用不同风格的意大利面条编码,所有这些都可以使用创建非条件分支。虐待的一些例子:
goto
被视为非常糟糕如果用于除跳跃之外的任何其他内容,例如用于错误处理程序。 (虽然这可能是goto
的可接受用途,但它仍然开始了整个被殴打死马的goto被认为是有害的辩论,所以我仅仅因为这个原因而避免goto
。)
continue
无条件地向上跳跃,这被认为是错误的:意大利面条代码的一个定义是无条件地向上和向下跳跃的代码。将多个continue
添加到同一循环时尤其糟糕。通常,continue
的存在是一个需要重写的循环的非常确定的标志。
可以滥用多个break
和多个return
来突破复杂的嵌套循环,使代码难以阅读和维护。此外,问题中演示的break
方法强制使用稍微模糊的do-while(false)循环。
通常,所有这些方法都被认为是不好的做法,因为它们很容易被滥用。你只需选择最不好的那个:换句话说,最可读和最不模糊。
我相信多个returns
是最易读的形式,因为它可以与一些函数结果变量混合使用,无论如何你都可能想要它:
result_t func ()
{
if (a)
{
doA();
return RESULT_A;
}
... // you'll need lots of if statements here to justify this program design
if (z)
{
doZ();
return RESULT_Z;
}
return RESULT_NORMAL;
}
关于函数内部资源分配的附注。
如果上面的函数需要释放一些已分配的资源并且它总是需要这样做,你应该用RAII来做,这样当局部变量超出范围时,它会自行清理。
如果它只需要在某些情况下释放一些已分配的资源(例如在出错时),那么你应该不在每个if语句(不可维护)中执行此操作,也不应该实现某些1980年代的BASIC程序员的“错误转到”。相反,考虑在main函数之外添加一个包装函数,以使程序可维护并最小化混乱:
result_t wrapper ()
{
stuff = allocate(); // if needed
result_t result = func(stuff);
if(result == BAD)
{
deallocate(stuff);
}
return result;
}
答案 3 :(得分:1)
你的非循环对你的程序员来说有点侮辱,但并非如此。在断言宏和记录器的实现中你会看到很多这样的代码。
当然,使用模板,滥用的机会几何扩展。例如,你可以写这种东西:
void doA() {
std::cout << "A" << std::endl;
}
void doB() {
std::cout << "B" << std::endl;
}
void either_or() {}
template<class T>
void either_or(T&& t) {
if (std::get<bool>(t)) {
std::get<void(*)()>(t)();
}
}
template<class T, class...Ts>
void either_or(T&& t, Ts&&... ts)
{
if (std::get<bool>(t)) {
std::get<void(*)()>(t)();
}
else {
either_or(std::forward<Ts>(ts)...);
}
}
auto main() -> int
{
using namespace std;
bool a = false;
bool b = true;
either_or(make_tuple(a, &doA),
make_tuple(b, &doB));
return 0;
}
虽然执行相应标志为真的第一个操作的一种有效方式(在优化者干预之后)对维护者来说可能更具辱骂性。
答案 4 :(得分:-1)
恕我直言,我认为使用goto
时没有任何错误。 如果您知道,使用goto
的方式,地点和原因,您的代码可能会更清晰,更易读。
例如,Linux使用了很多正如@Bartek Banachewicz所述,“Linux代码已经有了这个事实并不意味着他们是不那么糟糕。“ goto
语句。
goto
并不总是坏的,它们有许多有效的用途,比如打破深度嵌套的循环。在这种情况下使用goto
将呈现更清晰的代码。只有在被滥用或使用时才会导致意大利面条代码,而这些代码在C ++程序中并不相同。
注意:不必要地使用任何循环,if-else语句,break,continue 什么不是,将导致凌乱和无法管理的代码。即使我们 让这个世界成为
un-goto
的地方,意大利面条代码将存在。它的 个人以确保他/她写一个更清洁,可读, 最佳代码。
最好的设计是在C ++程序中使用exceptions
。
如果您不能选择使用例外,那么另一种设计可能是使用另一种方法,其中多个案例组合在一起。