跳过代码不使用状态变量,使用goto设想

Skip code without using state variable, using goto envisaged

本文关键字:变量 使用 goto 设想 状态 代码      更新时间:2023-10-16

我有一个代码,其中有部分不能执行,如果有一个错误之前的代码。我实际上使用了一个名为EndProg的bool变量,如果将其设置为true,将指示程序避免执行某些部分的代码。

我的问题是,我不想使用这种方法,我宁愿使用goto代替,因为它会使程序跳转到清理部分,避免多次检查EndProg值。

另一个问题是,我在StackOverflow和其他网站的许多页面上读到,使用goto被认为是一种不好的做法,它可以使代码更难以阅读或产生错误。

我的代码很简单,我只需要使用一个标签,所以我怀疑这会产生问题;但我想知道是否有其他方法可以做我想做的事情,而不需要创建函数来执行清理任务或使用return(因为,例如,我需要多次编写清理代码),我也不想在多个地方编写相同的大清理代码,然后使用return或做其他事情。

我不想增加代码行数,也不想使用return或使用大量if,也不想检查状态变量的值。你有什么建议吗?

下面是一段代码:

bool EndProg=false;
/*
    Lot of code that can set EndProg to true
*/
ClassType ClassName;
if(!EndProg && LoadConf(&ConfFilePath,&ClassName)==0)
{
    int fildes=-1;
    if(ClassName.abc) // bool
    {
        if(ClassName.FilePath==0) // char *
        {
            ClassName.FilePath=new(std::nothrow) char[9]();
            if(ClassName.FilePath!=0)strcpy(ClassName.FilePath,"file.ext");
            else EndProg=true;
        }
        if(!EndProg && mkfifo(ClassName.FilePath,S_IRUSR | S_IWUSR)==-1)
        {
            if(errno==EEXIST)
            {
                /* EEXIST is returned if the file already exists
                We continue, later we will try to open this file */
            }
            else EndProg=true;
        }
        if(!EndProg && (fildes=open(ClassName.FilePath,O_RDWR))==-1)EndProg=true;
    }
    /*
    Lot of code that will check if EndProg == true
    */
}
delete[] ClassName.FilePath;
delete[] ConfFilePath;

我想做的是:

bool EndProg=false;
/*
    Lot of code that can set EndProg to true
*/
ClassType ClassName;
if(LoadConf(&ConfFilePath,&ClassName)==0)
{
    int fildes=-1;
    if(ClassName.abc) // bool
    {
        if(ClassName.FilePath==0) // char *
        {
            ClassName.FilePath=new(std::nothrow) char[9]();
            if(ClassName.FilePath==0)goto cleanup;
            strcpy(ClassName.FilePath,"file.ext");
        }
        if(mkfifo(ClassName.FilePath,S_IRUSR | S_IWUSR)==-1)
        {
            if(errno==EEXIST)
            {
                /* EEXIST is returned if the file already exists
                We continue, later we will try to open this file */
            }
            else goto cleanup;
        }
        if((fildes=open(ClassName.FilePath,O_RDWR))==-1)goto cleanup;
    }
    /*
    Lot of code that will check if EndProg == true
    */
}
cleanup:
delete[] ClassName.FilePath;
delete[] ConfFilePath;

正如你所看到的,这并不难理解,即使搜索标签对某些人来说可能是一个问题,但对我来说不是;我不打算把代码公开。

更新:

我决定使用异常,它对我原始代码的某些部分有效。但我怀疑这在更复杂的部分是否容易实现。谢谢你的回答。

既然你已经标记了这个问题c++,我也会使用异常和try catch块。

你可以在SO和其他网站上找到很多关于这个主题的有用信息:

这是一个非常基础的教程。

这里有一个很好的基本的常见问题解答,可能也会对你有所帮助。

基本上没有什么好害怕的,异常并不神秘,事实上,当你掌握了它的窍门时,它更有意义。因为基本上这个概念可以让你实现你想要的:

可以由相同的错误处理代码处理的几个缺陷。

编辑:例如,如果我将mkfifo等移动到一个函数中(通常为每个定义良好的逻辑块创建一个函数更清晰,更可读),并具有类似

的内容

这只是一个草图给你一个大致的概念:

#include <exception>

functionThatDoesMakeFifo(...){
   // check which ever conditions you want to check after mkfifo
   // if one of them goes wrong just do:
   throw std::exception();
}
// this is inside your function:
   ClassType ClassName;       
   try{
      ClassName.FilePath = new char[9](); // even though I'd use a string...
      .
      .
      . // rest of the code
   } catch(std::exception &e){    
       delete [] ClassName.FilePath;
       delete [] ConfFilePath;
       ClassName.FilePath = NULL; // not mandatory just my habit
       ConfFilePath = NULL;
   }

我会尝试使用范围保护或BOOstrongCOPE_EXIT (c++)或其c++ 11类似的东西:

template<class F>
struct ScopeExit
{
  ScopeExit(F f) : f(f) {}
  ~ScopeExit() { f(); }
  F f;
};
template<class F>
ScopeExit<F> MakeScopeExit(F f) { return ScopeExit<F>(f); }
#define STRING_JOIN2(arg1, arg2) DO_STRING_JOIN2(arg1, arg2)
#define DO_STRING_JOIN2(arg1, arg2) arg1 ## arg2
#define SCOPE_EXIT(code) 
  auto STRING_JOIN2(scope_exit_, __LINE__) = MakeScopeExit([=](){code;})

bool myfunct()
{
  ClassType ClassName;
  ClassName.FilePath = 0;
  ConfFilePath = 0;
  SCOPE_EXIT(delete [] ClassName.FilePath; delete [] ConfFilePath; );
  if (LoadConf(&ConfFilePath,&ClassName) == 0)
  {
    int fildes=-1;
    if(ClassName.abc) // bool
    {
      if(ClassName.FilePath==0) // char *
      {
        ClassName.FilePath=new(std::nothrow) char[9]();
        if(ClassName.FilePath==0) return false;
        strcpy(ClassName.FilePath,"file.ext");
      }
      if(mkfifo(ClassName.FilePath,S_IRUSR | S_IWUSR)==-1)
      {
        if (errno==EEXIST)
        {
          /* EEXIST is returned if the file already exists
          We continue, later we will try to open this file */
        }
        else return false;
      }
      if((fildes=open(ClassName.FilePath,O_RDWR))==-1) return false;
    }
    /*
    Lot of code that will check if EndProg == true
    */
  }
  return true;
}

我使用return,但清理代码只是在一个地方。

无论如何,ClassName应该负责清理析构函数中自己的资源。

我以前见过一个小技巧可以帮助您解决这个问题,尽管我个人不喜欢技巧,但它可能适合您的需要。

while (true)
{
    if(ClassName.FilePath==0) // char *
    {
        ClassName.FilePath=new(std::nothrow) char[9]();
        if(ClassName.FilePath==0) break;
        strcpy(ClassName.FilePath,"file.ext");
    }
    if(mkfifo(ClassName.FilePath,S_IRUSR | S_IWUSR)==-1)
    {
        if(errno==EEXIST)
        {
            /* EEXIST is returned if the file already exists
            We continue, later we will try to open this file */
        }
        else break;
    }
    if((fildes=open(ClassName.FilePath,O_RDWR))==-1) break;
    /*
    Lot of code that will check if EndProg == true
    */
    break;
}
delete[] ClassName.FilePath;
delete[] ConfFilePath;

但是我也不认为这是一个优雅的解决方案,我个人会重写你的代码,把它分解成更容易读的东西。但是,我也不写包含数百行的函数。

我可能会被投反对票,但我认为在C语言中有限地使用goto并不是坏事。特别是,您所谈论的是完全可以接受的:向前分支以清除错误代码。我建议您将此限制为每个例程一个目标标签。

人们讨厌的(有理由的)是老式的意大利面条式代码,到处都是goto。