在编程实践中总会遇到处理有大量分支情况,即根据不同的条件进行不同的处理。因为最近做项目时遇到过这样的情景,总觉得在代码中写一大堆if-ele-if分支代码块不是很优雅,所以在此记录一下大量if-else-if代码块的优化情况。
1、Level1:if-else-if分支(原始)
当程序中有大量的分支情况进行处理时,if-else-if分支结构总是大量使用,switch-case分支结构也是经常使用的,但是前者的应用面更广一些。
下面就是应用if-else-if分支结构进行处理的情况:
class NumSeq
{
public:
void func1(int);
void func2(int);
void func3(int);
//...
void run_seq(int);
};
void NumSeq::func1(int i_seq)
{
cout << "调用函数 -- func1() " << " 传递参数: i_seq: " << i_seq << endl;
}
void NumSeq::func2(int i_seq)
{
cout << "调用函数 -- func2() " << " 传递参数: i_seq: " << i_seq << endl;
}
void NumSeq::func3(int i_seq)
{
cout << "调用函数 -- func3() " << " 传递参数: i_seq: " << i_seq << endl;
}
void NumSeq::run_seq(int i_seq)
{
if (1== i_seq )
{
this->func1(i_seq);
}
else if (2== i_seq )
{
this->func2(i_seq);
}
else if (3== i_seq )
{
this->func3(i_seq);
}
else
{
cout << "Wrong i_seq.\n";
}
}
在成员函数run_seq()中,通过if-else-if对输入的i_seq进行判断,根据不同的值执行不同的函数。这是最常用的一种用法,但是,当i_seq的情况有很多,而对应的执行函数也有很多时,再使用if-else-if就会造成在run_seq()函数中堆积了大量的if-else-if代码块,看起来不是很优雅。
2、Level2:使用类成员函数指针进行优化 - 1
针对上述的问题,有一种解法是通过指向类成员函数的指针进行解决。首先介绍一下该指针。
2.1 指向类成员函数的指针(Pointers to Class Member Functions)的声明
pointer to member function(指向成员函数的指针),这种指针看起来和pointer to non-member function极为相似。两者皆需要指定其返回类型和参数列表。不过pointer to member function还需要指定它所指的究竟是哪一个class。例如以下定义方式:
void (Foo::*pmf)(int) = 0; //----------------------------------------------------------------------(1)
上述声明方式,便是将pmf声明为一个指针,该指针指向一个类Foo的成员函数,后者的返回类型必须是void,并只能接收单个int类型的参数。pmf的初始值设置为了0,表示它目前并不指向任何成员函数。可以通过以下方式简化语法:
typedef void (Foo::*Ptrtype)(int); //----------------------------------------------------------------------(2)
Ptrtype pmf = 0;
上面的这种声明方式(2)和(1)中的声明完全等价。
2.2 指向类成员函数的指针(Pointers to Class Member Functions)的赋值
要对指向成员函数的指针进行赋值,首先要去的某个成员函数(member function)的地址,我们采用&取地址符,通过对函数名称应用&(address-of)运算符,即可取得成员函数的地址(这种取值方式和pointer to non-member function是有区别的,后者只需要用名称进行赋值即可)。但是需要注意的是,在函数名称之前必须先以class scope运算符加以限定,至于返回类型和参数列表皆可不必指明,如下所示:
Ptrtype pmf = &Foo::func1; //初始化pmf
注意,函数体一定要定义!函数体一定要定义!函数体一定要定义!不然直接调用 &Foo::func2 语句会报错!
同样如果是对pmf赋值,也应该采用上述方式:pmf = &Foo::func2;
2.3 指向类成员函数的指针(Pointers to Class Member Functions)的使用
pointer to member function 和 pointer to non-member function 在使用时的一个不同点是,前者必须通过同一类的对象加以调用,而对象便是此member function内的this指针所指之物,并且语法上也有些许不同,如下所示:
Foo foo;
Ptrtype pmf = &Foo::func1;
为了通过foo调用pmf,可以这样写:
(foo.*pmf)(i_num); //和常规调用成员函数不同,在函数名之前必须添加*。这个*符号是个pointer to member selection运算符,系针对class object工作。而且必须为它加上外围小括号()才能正确工作。
下面是针对pointer to class object的调用用法:
Foo *p_foo = &foo;
(p_foo->*pmf)(i_num); //必须添加*和()
2.4 使用 Pointers to Class Member Functions 优化
使用 Pointers to Class Member Functions 对if-else-if进行优化,代码如下所示:
class NumSequence
{
public:
//定义了一种函数类型:指向成员变量,有一个Int型参数,返回void
typedef void (NumSequence::*Ptrtype)(int);
//_pmf可指向下列任何一个函数
void func1(int);
void func2(int);
void func3(int);
//...
void run_sequence(int);
private:
Ptrtype _pmf; //定义了一个 Ptrtype 类型的变量:_pmf,是一个指向类成员函数的指针
};
void NumSequence::func1(int i_seq)
{
cout << "调用函数 -- func1() " << " 传递参数: i_seq: " << i_seq << endl;
}
void NumSequence::func2(int i_seq)
{
cout << "调用函数 -- func2() " << " 传递参数: i_seq: " << i_seq << endl;
}
void NumSequence::func3(int i_seq)
{
cout << "调用函数 -- func3() " << " 传递参数: i_seq: " << i_seq << endl;
}
void NumSequence::run_sequence(int index)
{
switch (index)
{
case 1:
_pmf = &NumSequence::func1; //类成员函数指针的赋值方式
break;
case 2:
_pmf = &NumSequence::func2;
break;
case 3:
_pmf = &NumSequence::func3;
break;
default:
_pmf = nullptr;
break;
}
if (_pmf)
{
//调用类成员指针的方式:(pC_reference->*pmf)(pars...) 或者 (C_reference.*pmf)(pars...)
(this->*_pmf)(index);
}
}
在上述代码中,通过run_sequence()函数,根据不同的index将不同的成员函数指针赋值给_pmf,然后再通过_pmf指针进行函数调用。
但是上述代码并没有很好的优化if-else-if分支结构,只是一个引子,引出下面的优化方式。
3、Level3:使用类成员函数指针进行优化 - 2
通过第2章节应该对指向成员函数的指针有了一个基本的了解,那怎么样才能更好的优化这个分支结构呢?
首先思考一个问题,指向成员函数的指针虽然和普通的函数指针有些许不同,但它本质上还是一个指针,是指针,我们就可以把它存储起来,当有大量的指向成员函数的指针时,我们就用一个容器把它存储起来这样就可以根据需要提取不同的指针(该指针指向不同的成员函数),然后就可以通过该指针执行函数的调用。这样一想,就会发现它的使用和普通的函数指针没什么两样。
所以为了更好的优化if-else-if分支结构,可以采用map容器进行存储,其中key用来存储index,也可以是其他的类型主要是为了区别不同地操作,value用来存储指向成员函数的指针。这样当我们获得一个key之后,就可以直接通过map获取对应的函数指针,就可以进行操作了。具体代码实现如下所示:
#include <map>
class advNumSequence
{
public:
//定义了一种函数类型:指向成员变量,有一个Int型参数,返回void
typedef void (advNumSequence::*Ptrtype)(int);
advNumSequence()
{
func_map[1] = &advNumSequence::adv_func1;
func_map[2] = &advNumSequence::adv_func2;
func_map[3] = &advNumSequence::adv_func3;
}
//_pmf可指向下列任何一个函数
void adv_func1(int);
void adv_func2(int);
void adv_func3(int);
//...
void run_sequence(int);
private:
Ptrtype _pmf; //定义了一个 Ptrtype 类型的变量:_pmf,是一个指向类成员函数的指针
map<int, Ptrtype> func_map; //定义一个map。key为index,value为index对应的Ptrtype类型的类成员函数
};
void advNumSequence::adv_func1(int i_seq)
{
cout << "调用函数 -- adv_func1() " << " 传递参数: i_seq: " << i_seq << endl;
}
void advNumSequence::adv_func2(int i_seq)
{
cout << "调用函数 -- adv_func2() " << " 传递参数: i_seq: " << i_seq << endl;
}
void advNumSequence::adv_func3(int i_seq)
{
cout << "调用函数 -- adv_func3() " << " 传递参数: i_seq: " << i_seq << endl;
}
void advNumSequence::run_sequence(int index)
{
this->_pmf = this->func_map[index]; //取出对应的函数指针
(this->*_pmf)(index);
}
上述代码,在类的构造函数advNumSequence中 保存了成员函数的指针以及它们对应的key。这样在run_seqence()函数中,就可以直接通过key获取对应的函数指针,然后进行调用就可以了。