简化代码,C/C++函数指针数组实现菜单系统
—————————————————————————————————————————
我们经常会遇到使用C/C++语言编写一个菜单系统的情况。在控制台窗口打印一个有好多选项的菜单,然后提示你通过输入对应的编号来完成相应的操作。
这不,我们数据结构课,老师也是叫我们把一种数据结构的所有操作输出到一个菜单里,通过输入序号来测试每个操作的功能。老师给出了我们大体上框架的代码,但是老师使用的是switch来执行菜单,代码较多重复,相当麻烦。于是我就使用函数指针数组来替代实现了相同的功能。
——————————————————————————————————————————
本文的写作背景
这是我们作业里要实现的效果(当然,本文的目的不是介绍数据结构):
输入对应序号之后,老师的main函数中代码是这样的:
cin>>choose;//整型 if(choose>0&&choose<14) { system("cls");//VC的清屏函数,gcc不适用 displayCurrentObject(sq);//打印当前顺序表的每个元素 } switch(choose) { case 1:ex3_1_1(sq,continueYesNo);break; case 2:ex3_1_2(sq,continueYesNo);break; case 3:ex3_1_3(sq,continueYesNo);break; case 4:ex3_1_4(sq,continueYesNo);break; case 5:ex3_1_5(sq,continueYesNo);break; case 6:ex3_1_6(sq,continueYesNo);break; case 7:ex3_1_7(sq,continueYesNo);break; case 8:ex3_1_8(sq,continueYesNo);break; case 9:ex3_1_9(sq,continueYesNo);break; case 10:ex3_1_10(sq,continueYesNo);break; case 11:ex3_1_11(sq,continueYesNo);break; case 12:ex3_1_12(sq,continueYesNo);break; case 13:ex3_1_13(sq,continueYesNo);break; default: cout<<"\n 你选择了结束"<<endl<<endl; return; } //判断是否继续操作 if(continueYesNo=='N'||continueYesNo=='n') break;
我们自定义的顺序表类中封装的每一个操作都单独编写了一个测试函数来测试这些操作,比如:
//ex3_1_Test.h单独的一个头文件 //测试:判断顺序表是否为空 template <typename ElemType> void ex3_1_2(MySqList<ElemType>& sq,char& continueYesNo) { cout<<" ***********\4判断顺序表是否为空\4**********"<<endl<<endl; if(sq.isEmpty()) cout<<"当前顺序表为空"<<endl; else { cout<<"当前顺序表不为空"<<endl; cout<<sq; } cout<<" *****************************************"<<endl<<endl; cout<<" 还继续吗(Y.继续\tN.结束)?"; cin>>continueYesNo; }当然,这些都不是本文的重点,只不过是作为我写作本文的一个背景。
函数指针数组替代switch
我们看到每一个测试函数ex3_1_……都有相同的形参列表:一个自定义的模板MySqList对象的引用,和一个用于判断是否继续的char型continueYesNo。这也是我们老师搭的框架。然而switch语句下面十几个case着实让人看着不舒服,因为它们每一个都实在是太像了。所以我改用了函数指针的数组来实现。
//main函数所在的cpp文件中 void (*test[13])(MySqList<int>&,char&)={ex3_1_1,ex3_1_2,ex3_1_3,ex3_1_4, ex3_1_5,ex3_1_6,ex3_1_7,ex3_1_8, ex3_1_9,ex3_1_10,ex3_1_11,ex3_1_12, ex3_1_13};
然后原来冗长的switch语句就变成了:
cin>>choose; if(choose>0&&choose<14) { system("cls"); displayCurrentObject(sq); } test[choose-1](sq,continueYesNo); if(continueYesNo=='N'||continueYesNo=='n') break;请注意函数指针数组的写法。
void (*test[])(形参列表)={……};如果在声明的时候初始化,那么[ ]中可省略数组大小,就像普通数组一样。其实只要能理解函数指针,那么函数指针的数组也就一目了然了。
小讲解函数指针及其数组
普通的函数指针,比如我们有三个判断大小的函数:
//功能:判断两整数是否相等 bool equal(int a,int b) { if(a==b) return true; else return false; } //功能:判断一个整数是否大于另一个整数 bool great(int a,int b) { if(a>b) return true; else return false; } //功能:判断一个整数是否小于另一个整数 bool less(int a,int b) { if(a<b) return true; else return false; }这是三个具有相同形参列表的函数。有时候我们需要动态的执行这三个操作,比如由用户决定,,或者其他情况需要我们把函数本身作为参数传递的时候。
假设我们有一个函数fun用于比较两个整型,而具体是何种比较则作为一个形参,我们可以写成:
bool fun(int a,int b,bool(*compare)(int,int)) { return (*compare)(a,b); }看懂了吗,其实万变不离其宗,bool(*compare)(int,int)看起来虽然复杂,其实也是一个形参的类型加上一个该类型形参的变量名,就好比是 int a 一样,a只是一个整型变量,起什么名字无所谓。bool(*compare)(int,int)分解开也是两部分:
bool(*)(int,int) 是参数的类型,compare是该类型的变量名,也是起什么名字无所谓的。再看指针数组比如:
bool(*fun[3])(int,int)={equal,great,less};就好理解了吧,同样是在变量名后面加上[ ] 来代表数组。上面的代码也等价于:
bool(*fun2[3])(int,int); fun2[0] = equal; fun2[1] = great; fun2[2] = less;或许有人会心生疑问,这里的fun2[ ]数组里面应该包含的是指针啊,能直接把函数名赋值给它吗?没错是可以的实际上函数的函数名本身就是一个指针类型。
//这是等效的操作 fun2[0] = equal; fun2[0] = &equal; //注意!这里的&是取地址符号,而非引用符号