使用goto干净地退出循环

Hun*_*ter 6 c++ goto while-loop

我有一个关于在C++中使用goto语句的问题.我理解这个话题是有争议的,并且对任何广泛的建议或论点都不感兴趣(我通常偏离使用goto).相反,我有一个特定的情况,并想要了解我的解决方案,它使用goto语句,是否是一个好的.我不会称自己是C++的新手,但也不会将自己归类为专业级程序员.生成我的问题的代码部分一旦启动就会在无限循环中旋转.伪代码中线程的一般流程如下:

void ControlLoop::main_loop()
{
    InitializeAndCheckHardware(pHardware) //pHardware is a pointer given from outside
    //The main loop
    while (m_bIsRunning)
    {
        simulated_time += time_increment; //this will probably be += 0.001 seconds
        ReadSensorData();
        if (data_is_bad) {
            m_bIsRunning = false;
            goto loop_end;
        }    
        ApplyFilterToData();
        ComputeControllerOutput();
        SendOutputToHardware();
        ProcessPendingEvents();

        while ( GetWallClockTime() < simulated_time ) {}
        if ( end_condition_is_satisified ) m_bIsRunning = false;
    }
    loop_end:
    DeInitializeHardware(pHardware);
}
Run Code Online (Sandbox Code Playgroud)

pHardware指针从ControlLoop对象外部传入并具有多态类型,因此对我来说使用RAII并在main_loop中创建和破坏硬件接口本身没有多大意义.我想我可以让pHardware创建一个临时对象,代表硬件的"会话"或"使用",可以在main_loop退出时自动清理,但我不确定这个想法是否会让某些人更清楚否则我的意图是什么.循环中只有三种方式:第一种方法是从外部硬件读取坏数据; 第二个是如果ProcessPendingEvents()指示用户启动的中止,这只会导致m_bIsRunning变为false; 最后一个是如果在循环的底部满足结束条件.我还应该注意,main_loop可以在ControlLoop对象的生命周期内多次启动和完成,因此它应该在m_bIsRunning = false之后干净利落地退出.

另外,我意识到我可以在这里使用break关键字,但是main_loop中的大多数伪代码函数调用并没有真正封装为函数,只是因为它们需要有很多参数,或者它们都需要访问成员变量.在我看来,这两种情况都比让简单地将main_loop作为一个更长的函数更容易混淆,而且由于big while循环的长度,一个类似的声明goto loop_end似乎对我来说更清晰.

现在提出一个问题:如果您在自己的代码中编写它,这个解决方案会让您感到不舒服吗?它对我来说确实有些不对劲,但之后我从未在C++代码中使用过goto语句 - 因此我请求专家提供帮助.我还缺少哪些其他基本想法可以使这些代码更清晰?

谢谢.

pic*_*ypg 5

goto在一般的面向对象开发中,避免使用是非常可靠的事情.

在你的情况下,为什么不只是break用来退出循环?

while (true)
{
    if (condition_is_met)
    {
        // cleanup
        break;
    }
}
Run Code Online (Sandbox Code Playgroud)

至于你的问题:你的使用goto会让我感到不舒服.break不太可读的唯一原因是你不能成为一名优秀的C++开发人员.对于任何经验丰富的类似C语言的开发人员来说,break两者都会更好地阅读,并提供更清晰的解决方案goto.

特别是,我根本不同意

if (something)
{
    goto loop_end;
}
Run Code Online (Sandbox Code Playgroud)

比...更具可读性

if (something)
{
    break;
}
Run Code Online (Sandbox Code Playgroud)

字面意思是内置语法相同的东西.

  • @Hunter:*我主要担心的是实际代码中的主要while循环非常长*=>当一个函数太长而无法在屏幕上舒适地适应时,是时候开始将它分解成更小的函数了; 你在这一点上使用`goto`或`break`是无关紧要的. (2认同)

Ed *_* S. 3

对于导致循环提前中断的单一条件,我将简单地使用break. 不需要,goto这就是break目的。

但是,如果这些函数调用中的任何一个都可能引发异常,或者如果您最终需要多个breaks,我会更喜欢 RAII 样式容器,这正是析构函数的用途。您总是执行对 的调用DeInitializeHardware,所以...

// todo: add error checking if needed
class HardwareWrapper {
public:
    HardwareWrapper(Hardware *pH) 
      : _pHardware(pH) { 
        InitializeAndCheckHardware(_pHardware);
    }

    ~HardwareWrapper() {
        DeInitializeHardware(_pHardware);
    }

    const Hardware *getHardware() const {
        return _pHardware;
    }

    const Hardware *operator->() const {
        return _pHardware;
    }

    const Hardware& operator*() const {
        return *_pHardware;
    }

private:
    Hardware *_pHardware;
    // if you don't want to allow copies...
    HardwareWrapper(const HardwareWrapper &other);
    HardwareWrapper& operator=(const HardwareWrapper &other);
}

// ...

void ControlLoop::main_loop()
{
    HardwareWrapper hw(pHardware);
    // code
}
Run Code Online (Sandbox Code Playgroud)

现在,无论发生什么,您总是会DeInitializeHardware在该函数返回时调用。