使用指向成员的指针数组或开关更好吗? 在我的学校里,我们非常鼓励在C++中使用指针数组来代替成员(或多个if)。

使用指向成员的指针数组或开关更好吗? 在我的学校里,我们非常鼓励在C++中使用指针数组来代替成员(或多个if)。,c++,optimization,switch-statement,function-pointers,C++,Optimization,Switch Statement,Function Pointers,由于我不认为使用这样的数组(实际上我使用指向成员的指针映射)而不是switch语句有任何意义,我想知道是否真的有任何类型的优化建议指向函数 以下是让我觉得最好使用switch的原因: 指向成员的指针数组(尤其是映射)内存很重(std::string作为键,指针作为值),需要存储在类中(因为它不是对象属性,所以没有任何意义…),或者每次在函数中使用它们重新创建(如果静态声明): std::map<std::string, void (MyClass::*)(...)> opera

由于我不认为使用这样的数组(实际上我使用指向成员的指针映射)而不是switch语句有任何意义,我想知道是否真的有任何类型的优化建议指向函数

以下是让我觉得最好使用switch的原因:

  • 指向成员的指针数组(尤其是映射)内存很重(std::string作为键,指针作为值),需要存储在类中(因为它不是对象属性,所以没有任何意义…),或者每次在函数中使用它们重新创建(如果静态声明):

    std::map<std::string, void (MyClass::*)(...)>   operations;
    
那么,为什么我们不得不使用指向成员的指针,而不仅仅是一个clear switch语句,或者甚至是ifs


谢谢。

您对指向成员函数的指针数组与开关指令相比的优缺点的分析已经非常好了

但这一切都取决于环境:

  • 当然,从技术上讲,您完全正确:如果您只想更换一个交换机,那么这样的阵列非常麻烦。更不用说编译器了,它可以通过使用比数组更少的间接寻址来优化开关

  • 但是您的示例代码实现了一种。从设计的角度来看,这在进化性和可维护性方面具有实质性优势,超过了技术缺陷。例如,可以在应用程序中轻松使用它来实现撤销/重做功能。它还简化了多个同时的用户界面允许在对象上触发这些命令的情况(例如:命令行窗口和GUI)


视情况而定。具有多个,未连接选项的开关盒实际上与大的
相同,否则
-慢。好的优化是使用建议您实现的偏移表(或跳转表)执行所需的操作

奇怪的是,编译器通常可以自动执行这种优化——如果
开关盒
写得好的话

但是写得好意味着什么呢

这意味着,您必须设计索引项,以便轻松快速地计算需要执行的索引项的位置。考虑下面的代码:

int n = 0;
std::cin >> n;

if(n == 1) printf("1\n");
else if(n == 2) printf("2\n");
else if(n == 3) printf("3\n");
else if(n == 4) printf("4\n");
这是可能的输出(实际输出,在VC11上,用/O2编译):

基本上是一个
if-else
。现在,让我们更改代码:

int n = 0;
std::cin >> n;

switch(n)
{
case  1: printf("1\n"); break;
case  2: printf("2\n"); break;
case  3: printf("3\n"); break;
case  4: printf("4\n"); break;
}
可能的产出:

011BA799  mov         eax,dword ptr [n]  // switch case will run if n is 1-4
011BA79C  dec         eax //decrement by one, now it should be in 0-3
011BA79D  cmp         eax,3 // compare with 3
011BA7A0  ja          $LN4+46h (011BA7EFh) //if greater than 3, skip switch
011BA7A2  jmp         dword ptr [eax*4+11BA7F8h] //otherwise compute offset of instrcution and jump there
我没有发布对
printf
的调用-基本相同,但没有任何
cmp
或跳转指令

当然,这个输出只是许多可能的输出之一,但关键是:设计良好的应用程序,在条件节上进行智能优化,可以执行更高效的操作。在这里,编译器能够直接跳转到正确的指令,因为它可以很容易地计算其偏移量——所有的情况都用数字标记,并按1增长


更直接地回答您的问题:您得到的建议在技术上是正确的,但我将重点关注编译器友好的优化,而不是复杂的代码(这可能会或可能不会显著提高速度),这是每个人都无法理解和信赖的(只要编译器足够聪明,能够利用这一优势并生成优化的代码)。

上下文很重要

如果你使用电脑,我想你更喜欢阵列,因为与比较结果相比,获得结果的速度非常快,请购买这款内存。 这在内存上很昂贵,但在阵列非常大的情况下速度很快

如果环境是一个微控制器,内存非常昂贵,你不能浪费去保存所有的阵列,尤其是当阵列几乎没有被使用的时候。 但是,由于不使用内存,而且微控制器中的汇编程序速度非常快,因此可以优先选择开关

  • 如果你有这么多的内存和高级编程语言,最好是数组
  • 若你们的内存非常少,而且低级编程语言是汇编语言,或者是C语言,最好是开关(或者rom表)

为什么不
操作[“push”]=&Parser::push
?-\uuuu-这是速度vs.空间。要么你花大量时间重复地做if/else/else/else/else/…,要么你在字典上花了一点开销。两者都有支持/反对的观点。你试过使用“仅仅是一个clear switch语句”吗在
std::string
类型上?也许这是你答案的一部分。@WhozCraig只是作为OP的参考,以防他好奇是否可以这样做:)好的,对于
操作[“push”]=&Parser::push
,我100%确定我添加了这个语法,因为你的解决方案没有编译,但它似乎工作正常,我不知道我脑子里发生了什么^^'事实上,std::string as键只是一个例子,我可以使用一个枚举,它可以在一个开关中使用。(vsofcto,感谢链接)。我认为在微控制器的环境中,内存非常昂贵,您不能浪费任何内存来进行切换。在我看到的大多数代码中,开关占用的内存比数组要多。您好,您提到“具有多个未连接选项的开关盒实际上与大开关盒相同”。“未连接”是什么意思?您的意思是这些案例不是连续的(例如0,1,59991024)?
011AA799  mov         eax,dword ptr [n]  
011AA79C  cmp         eax,1 //is n equal to 1?
011AA79F  jne         main+34h (011AA7B4h) //if yes, continue, if not, jump... [J1]
011AA7A1  push        1262658h  
011AA7A6  call        printf (011E1540h) // print 1
011AA7AB  add         esp,4  
011AA7AE  xor         eax,eax  
011AA7B0  mov         esp,ebp  
011AA7B2  pop         ebp  
011AA7B3  ret  
011AA7B4  cmp         eax,2 // [J1] ...here. Is n equal to 2?
011AA7B7  jne         main+4Ch (011AA7CCh) //If yes, continue, if not, jump... [J2]
011AA7B9  push        126265Ch  
011AA7BE  call        printf (011E1540h) // print 2
011AA7C3  add         esp,4  
011AA7C6  xor         eax,eax  
011AA7C8  mov         esp,ebp  
011AA7CA  pop         ebp  
011AA7CB  ret  
011AA7CC  cmp         eax,3 // [J2] ...here. Is n equal to 3? (and so on...)
011AA7CF  jne         main+64h (011AA7E4h)  
011AA7D1  push        1262660h  
011AA7D6  call        printf (011E1540h)
[...]
int n = 0;
std::cin >> n;

switch(n)
{
case  1: printf("1\n"); break;
case  2: printf("2\n"); break;
case  3: printf("3\n"); break;
case  4: printf("4\n"); break;
}
011BA799  mov         eax,dword ptr [n]  // switch case will run if n is 1-4
011BA79C  dec         eax //decrement by one, now it should be in 0-3
011BA79D  cmp         eax,3 // compare with 3
011BA7A0  ja          $LN4+46h (011BA7EFh) //if greater than 3, skip switch
011BA7A2  jmp         dword ptr [eax*4+11BA7F8h] //otherwise compute offset of instrcution and jump there