在C ++中使用嵌套开关是一个好习惯吗?有没有其他方法可以避免这种情况?

时间:2018-03-13 09:16:53

标签: c++ methods

目前我的函数有两个枚举类型的参数,该函数需要比较两者并执行特定任务 例如:

 void set_Test_Status(Tests TestName, Status TestStatus)
 {
    switch(TestName)
    {
         case Tests::Test1:
              {
                 switch(TestStatus)
                 {
                     case TestStatus::St1:
                     //Rest of Code
                 }
              }
           //Rest of Code
    }
 }

这是一个很好的编程习惯吗?或者我应该注意哪些替代方法或编码方式?谢谢!

编辑: 经过反复试验,我做到了这一点。首先,我看到每个枚举中的最大和最小项目,在我的情况下,TestName为6,TestStatus为3.我创建了3个函数setStatusRunning(Tests TestName),setStatusSelected(Tests TestName)和setStatusFinished(Tests TestName),并在set_Test_status中使用switch (TestName)我检查应该调用哪个函数然后调用适当的函数。我必须首先创建set_Test_Status的原因是为了让其他类变得容易,因为我将set_Test_Status设为public而另外3个为private。

3 个答案:

答案 0 :(得分:3)

由于嵌套的switch语句是有效的c ++代码,这确实是一个观点问题。有些人可能没有问题,而有些人则认为这可能会造成混淆。

我的建议是基于此;如果case语句中的代码块很短,例如1-2行,并且它们相当容易阅读和遵循,那么它应该没有任何问题。但是,如果代码非常繁琐,并且嵌套的switch语句跨越50到100多行,那么我建议改进你的代码并用它们来创建函数。

示例:

// should be okay

unsigned int outerSwitch = someValue(); 
unsigned int innerSwitch = someOtherValue();
switch ( outerSwitch ) {
    case 1: {
        switch ( innerSwitch ) {
            case 1 : {
                // 1 or 2 lines okay;  
            } 
            case 2 : {
                // 1 or 2 lines okay;
            }
            case 3 : {
                // 1 or 2 lines okay;
            }
            default : {

            }
        } // inner switch when outer switch case = 1
    }
    case 2: {
          // Same as case 1
    }
    case 3: {
          // Same as case 1
    }
    default: {

    }
} // outer switch

但正如你在上面看到的只有3例外在和内在;它很快就变得非常长,我甚至都没有扩展它们。所以这可以不受欢迎。

你可以有一个单一的开关,因为其他人建议调用该开关的特定功能,然后在该功能中它有自己的开关语句,如下所示:

unsigned int someFuncA( unsigned int someVal ) {
    switch ( someVal ) {
        case 1 : {
            // do this
            // return that;   
        }
        case 2: {
            // ... etc.
        }
    } // switch
} 

unsigned int someFuncB( int someVal ) {
    // similar to someFuncA();
}

unsigned int someFuncC( int someVal ) {
    // similar to someFuncA();
}

unsigned int switchValue = someValue();
unsigned int innerValue = someOtherFunction();
unsigned int temp = 0;
switch( switchValue ) {
    case 1 : {
        temp = someFuncA( innerValue );
        // either return temp, break, continue, or fall through
    }
    case 2 : {
        temp = someFuncB( innerValue );
        // same as above
    case 3 : {
        temp = someFuncC( innerValue );
        // same as above
    }
    default : {
        // default stuff here
    }   
} // switch

比较两者你会发现第二版比第一版更容易阅读,也不那么麻烦。是的,第一个版本是有效的合法C ++代码,但由于它可以轻松快速地获得多么混乱,因此不赞成。所以,你可以在哪里;将该代码转换为专门用于完成这一任务的函数。

修改

另一种可能性是设计特定的函数来完成一项任务,正如你所说的,你的函数有两个不同的枚举值,你可以查找设计用于取位标志的函数的概念。你会在Windows编程和OpenGL中看到这种代码。

另一种选择是:考虑到你有一个外部控制开关和内部控制开关。即使您有多个案例,每个案例都是独立的,具有特定的唯一ID。对于内部开关情况也是如此。知道了这一点,您可以使用std::multimap<unsigned, unsigned> testCases创建组合事务中所有语句的关联映射,并使用此查找表,您可以将它放在一个语句中,并对每个映射条目进行独立的函数调用。 cppreference : std::multimap

您的地图可能如下所示:

testCases[1][1];
testCases[1][2];
testCases[1][3];
testCases[2][1];
testCases[2][2];
testCases[2][3];
testCases[3][1];
testCases[3][2];
testCases[3][3];

地图的每个索引都是您要执行的逻辑或计算。

答案 1 :(得分:2)

对于阅读代码的人来说,这可能会让人感到困惑 - 缩进有所帮助,但在case和switch语句的开始和结束时仍然很难遵循。大多数IDE都支持查找匹配的括号,但找到匹配的中断通常不是一件事,所以甚至很难看到case语句的结束位置。 (技术上的情况并没有定义范围,而大括号也是如此,所以它甚至没有结束。)

如果你的牙箍未对齐或意外不匹配,你可能会发生奇怪的事情。像Ada这样的语言尝试使用强类型的英语说明符来防止这种情况。

如果你正在做很多子开关,我会把它们放到另一个函数中并用它需要的信息调用它,然后你可以在方法里面做一个switch语句,它更模块化,单独的范围和清楚的东西正在继续。

答案 2 :(得分:1)

保持代码简洁的一个好习惯是在函数中只做一件事。使用开关已经是一种气味,你的功能将会做不同的事情。这完全取决于您的具体情况。

要注意的一件事是:在一个班级中管理所有这些案例是否一致。如果每个可能性都有一个类,接口强制实现一个函数,而不是在switch case中做所有的可能性。

如果你想保留开关,一个好的做法是不要在开关之外放置代码。每个案例只调用一个函数。这将使东西更具可读性。

最后,您使用的是所有案例吗?如果不是,只需实现所需的值对,就可以更清晰地编写代码:

if(TestName == Tests::Test1 && TestStatus == TestStatus::St1)
{
    doThing();
}

总结:

这可能是一种气味,你的代码需要在更多的类中拆分,每个类只有一个责任(如果和/或太多的开关是太多的标志)。如果您确定需要开关,请尽可能简单明了