来源书籍:
Effective C++
C++ Primer
条款35:考虑virtual函数以外的其他选择
Template Method
对于一个继承体系来说,其中某一个功能可能会根据继承不同而实现不同,可以不直接使用虚函数。定义一个public的non-virtual成员函数,调用一个private virtual函数,private virtual函数如同一般的virtual函数。这一手法称为non-virtual interface手法,它是所谓的Template Method设计模式,这个non-virtual函数成为virtual函数的外覆器。
这样的好处是,可以在调用virtual之前做一些事前工作和调用之后做一些事后工作。这意味着外覆器确保得以在一个virtual函数被调用之前设定好适当场景,并在调用结束之后清理场景。
Strategy && function<>
利用函数指针/function<>模板,着重放在function<>上,他可以接受一个可调用对象。
callable object 包括传统C函数,C++成员函数,函数对象(实现了()运算符的类的实例),lambda表达式(特殊函数对象)
#include<iostream>
#include<functional>
class GameCharacter;
int defaultHealthCalc(const GameCharacter &c);
class GameCharacter {
typedef std::function<int(const GameCharacter&)> HealthCalcFunc;
public:
explicit GameCharacter(std::function<int(const GameCharacter&)>func = defaultHealthCalc) :healthfunc(func) {}
int healthValue() {
return healthfunc(*this);
}
protected:
HealthCalcFunc healthfunc;
};
class Level {
public:
Level(int n = 5):num(n){}
float health(const GameCharacter &c) {
std::cout << "Level Function : " << num << std::endl;
return -1;
}
private:
int num;
};
class Badguy :public GameCharacter {
typedef std::function<int(const GameCharacter&)> HealthCalcFunc;
public:
explicit Badguy(std::function<int(const GameCharacter&)>func = defaultHealthCalc){
healthfunc = func;
}
};
int defaultHealthCalc(const GameCharacter &c) {
std::cout << "Default Function" << std::endl;
return -1;
}
int main() {
GameCharacter a;
a.healthValue();
GameCharacter b([](const GameCharacter &b) {std::cout << "lambda" << std::endl; return -1; });
b.healthValue();
Level currentlevel(5);
Badguy c(std::bind(&Level::health, currentlevel, std::placeholders::_1));
c.healthValue();
getchar();
return 0;
}
此例中展示了function<int(const GameCharacter&)>
的用法,其中调用Level的成员函数时,用了bind绑定了对象和参数。
bind()
接受一个可调用对象,生成一个新的可调用对象来“适应”原对象的参数列表
绑定普通函数
例如:auto g = bind(f, a, b, _2, c, _1);
则调用g(x,y)的效果等同于调用f(a,b,y,c,x)
f可以是函数名也可以是函数指针
绑定成员函数
除了绑定参数列表,还可以把某个对象绑定到对应类上的成员函数上。但是成员函数隐含的需要一个*this来用,这样实际上隐含的讲该对象的*this传递给了该成员函数。
例如
Class A{
int f(int a,int b){…………}
};
A obj;
auto g = bind(&f, obj, 1, _1);
这样g(x)的效果等同为 obj.f(1,x);
注意,bind()时必须在成员函数前面加上取地址的操作符&表示这是一个成员函数指针。bind()同样支持绑定虚成员函数。