条款31
让函数根据一个以上的对象类型决定如何虚化
这里的问题很简单
class Base {
virtual void funtion( Base& base){}
};
class A : public base {};
class B : public base {};
class C : public base {};
现在,function 的参数都可能是A B C中的一个,但是不同的参数类型有不同的行为,如何根据参数类型决定funcion中的行为呢。因为是虚函数,所以在函数调用时,如 A.funtion(…),已经完成了一次消息派发,但是在不同派生类中的虚函数,还需要知道虚函数的参数类型,显然首先会想到RTTI,也就是说,可能会这么写:
class A : public base {
virtual void funtion(Base& base) {
if (typeid(base) == typeid(A)) {
} else if (typeid(base) == typeid(B)) {
} .....
}
};
这样做,的确可以完成,但是缺点很多,比如,添加或删除一个派生时,需要到所有派生类中修改,违反了开闭原则,而且,typeid对性能有影响。
第二种方法:
只使用虚函数:
class A : public Base {
virtual void funtion(A& ) {};
virtual void function(B& ) {};
...
};
使用函数重载,也完成了第二次类型派发(第一次是虚函数被调用),这里就好多了,没有RTTI, 没有if else,但是还有问题,还是违反了开闭原则。
第三种方法:
使用仿真的虚函数表格:
第一次类型派发由调用虚函数的对象来派发,问题是第二次,前面我们用了RTTI来逐个区分,用重载让编译来派发,这里是我们模仿虚函数的行为,自己来派发类型
class A : public Base {
public:
virtual void function(Base&) {}
virtual void AandB(B&) {}
virtual void AandC(C&) {}
using fptr = void (A::*)(Base&);
fptr findFunc(Base& type) {
static map<string, fptr> funcMap = init();
auto it = funcMap.find(typeid(type).name());
if (it != funcMap.end()) {
return it->second;
}
return nullptr;
}
static init() {
funcMap[typeid(A).name()] = ...
funcMap[typeid(B).name()] = ...
}
.....
};
这样,我们手动构造了一个函数指针的map,这样就在函数内部,通过map找到了需要的函数。
这里依然使用了RTTI, 不过对于这个,好像作者也没有给出好办法。这里也违反了开闭原则,不过作者给出了一种方法,将function函数内部的处理放到非成员函数中,在function 函数中只用findFunc函数用来查找非成员函数然后调用。