【學習筆記】條款35:考慮virtual函式以外的其他選擇 & bind & function模板
來源書籍:
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()同樣支援繫結虛成員函式。