2

几天来,我一直在搞乱 C++ SDL,遇到了一个有趣的问题。

SDL_Event event1;
while(SDL_WaitEvent(&event1))
{
    for(size_t i = 0; i < MainMenuOptions.size();i++)
    {
        if(event1.button.x > MainMenuOptions.at(i).GetX() && event1.button.x < (MainMenuOptions.at(i).GetX() + MainMenuOptions.at(i).GetWidth())  
        && event1.button.y > MainMenuOptions.at(i).GetY()  && event1.button.y < (MainMenuOptions.at(i).GetY() + MainMenuOptions.at(i).GetHeight()))
        {
            break;
        }
    }
}

当我在 for 循环中使用 break 时,它会跳出 for 循环而不是 while 循环。我如何在不使用 goto 语句的情况下打破 while 循环?(我听说 goto 语句是不好的编程)

4

5 回答 5

11

常见的解决方案是将这些东西放入它自己的函数中并从中返回:

inline SDL_Event do_it()
{
    SDL_Event event;
    while(SDL_WaitEvent(&event))
        for(std::size_t i = 0; i < MainMenuOptions.size(); ++i)
            if(/*...*/)
                return event;
    return event; // or whatever else suits, I know too little about your code
}
于 2012-08-20T08:56:09.403 回答
7

对此还有另一个答案,我想我应该在每个人都反对我之前说出来。使用变量当然是一种“好”的方法。然而,仅仅为了跳出循环而创建额外的变量似乎有点矫枉过正,对吧?

所以是的,这一次goto是完美的解决方案。很清楚你在用它做什么,你没有使用另一个变量,代码保持简短、可维护和可读。

语句goto is bad practice主要是 BASIC 时代的残余,当时它是更改代码流的唯一方法。然而,现在我们“知道得更好”,并说 thegoto或任何其他结构是坏的,只是没有削减它。对于您试图用它解决的一个特定问题可能会很糟糕(人们试图用 goto 解决的大多数问题就是这种情况)。但是,考虑到正确的情况(如这里),没关系。当然,我不想在这里开始辩论。Goto 就像一个非常强大的工具(例如大锤)。它有它的用途,你不能说一个工具是完全坏的。是用户以错误的方式使用它。

于 2012-08-20T08:18:14.527 回答
4

使用变量表示需要退出:

bool exit_program = false;

while( !exit_program && SDL_WaitEvent(&event1) )
{
    for( /* ... */ )
    {
        exit_program = true;
    }
}
于 2012-08-20T08:15:12.287 回答
4

第一点:IMO,你试图将太多东西包装到一个地方,并最终得到一些相当难以理解的东西——有人必须通读一整套长长的比较才能理解其中的任何内容应该完全完成。

第二点:使用显式循环遍历标准集合通常是错误的——这也不例外。标准库已经有一个算法来完成与循环相同的基本任务。最好使用它而不是自己重新编写它。

template <class T>
bool in_range(T a, T b, T c) { 
    return (a > b) && (a < b+c);
}

class in_rect {
   point p;
public:
   in_rect(point const &p) : p(p) {}

   // Not sure of the type of objects in MainMenuOptions, so just T for now.
   // 
   bool operator()(T const &m) {
       return in_range(p.x, m.GetX(), m.GetWidth()) 
           && in_range(p.y, m.GetY(), m.GetHeight());
   }
};

SDL_Event event1;

while (SDL_WaitEvent(&event1))
    if (std::any_of(MainMenuOptions.begin(), MainMenuOptions.end(),
                    in_rect(event1.button))      
        break;

一旦我们解决了其他问题,就不再需要(甚至使用)goto. 我们没有采取任何明确的措施来删除它,但是当其他问题得到解决(特别是用适当的算法替换循环)时,它的使用就消失了。

我想我应该抢先评论一下代码总行数的增加:是的,代码行数更多了。它呢?如果我们真的想要,我们可以使用相同的基本方法,但我们基本上只是从原始语句中获取条件并将其填充到 lambda 中,而不是定义in_rectand 。虽然我高兴将 lambdas 添加到 C++ 中,但在这种情况下,我对使用它并不感到兴奋。它将摆脱.in_rangeifgoto

简而言之,行数并不是衡量任何东西的好方法。

于 2012-08-24T15:20:39.447 回答
-1

没有额外变量的解决方案 和goto

while(SDL_WaitEvent(&event1))
{
    size_t i;
    for(i = 0; i < MainMenuOptions.size();i++)
    {
        if(/* ... */)
        {
            break;
        }
    }
    if (i < MainMenuOptions.size())
        break;
}
于 2012-08-20T08:38:09.000 回答