Effective C++ 总结[条款35 考虑virtual函数以外 的其他选择]
作者:互联网
假设写一个原神抽奖的函数,将玩家分为普通玩家、微氪玩家、氪金狂魔等,以下是所有类的基类:
class BaseTraveller { public: virtual void lottery(int num); };
其他玩家分类都可以重写该抽奖函数,以提供不同的抽奖算法。这是用virtual函数来实现的,现在本条款介绍了几种虚函数以外的实现方法。
1.藉由Non-Virtual Interface手法实现Template Method模式
首先介绍一下什么是Template Method模式(跟c++的模板template一点关系也没有哦, 是一种设计模式)。
Template Method,定义一个操作中算法的骨架,而将一些步骤延迟到子类中,使得子类可以不改变一个算法的结构即可重定义该算法的特定部分。
在此例中,算法的骨架就是lottery中的结构,它内部调用了doLottery这个核心部分,而子类可以重载这个部分使得抽奖算法在子类中表现出不同。
class BaseTraveller { public: void lottery(int num); private: virtual void doLottery(); }; class LittleRMBTraveller : public BaseTraveller { private: virtual void doLottery(); }; class MuchRMBTraveller : public BaseTraveller { private: virtual void doLottery(); }; // BaseTraveller void BaseTraveller::lottery(int num) { // do init doLottery(); // do exit } void BaseTraveller::doLottery() { // default method } // LittleRMBTraveller void LittleRMBTraveller::doLottery() { // more lucky method } // MuchRMBTraveller void MuchRMBTraveller::doLottery() { // unlucky method } // main.cpp BaseTraveller* pMuchRMB = new MuchRMBTraveller; pMuchRMB->lottery(10);
2.藉由函数指针实现Strategy模式
首先我介绍一下传统的Strategy设计模式。定义一系列算法,把它们封装为一个个单独的类,并且使他们可以相互替换。
class Lottery { // normal method public: virtual void doLottery(int) { }; }; class LuckyLottery : public Lottery { // lucky method public: virtual void doLottery(int) { }; }; class UnluckyLottery : public Lottery { // unlucky method public: virtual void doLottery(int) { }; }; class BaseTraveller { public: BaseTraveller(Lottery* p) : pLty(p) { }; void lottery(int num) { pLty->doLottery(num); } private: Lottery* pLty; }; class LittleRMBTraveller : public BaseTraveller { public: LittleRMBTraveller(Lottery* p) : BaseTraveller(p) { } }; class MuchRMBTraveller : public BaseTraveller { public: MuchRMBTraveller(Lottery* p) : BaseTraveller(p) { } }; // main.cpp LittleRMBTraveller littleRMB(new LuckyLottery); littleRMB.pLty->lottery(10);
以上是一个传统Strategy模式的一个例子,可以通过在构造函数中传入不同的抽奖算法,使得对于不同类别的旅行者有不同的行为。
那么现在进入正题,由一个函数指针来实现Strategy模式。
class BaseTraveller; void normalLottery(int num, BaseTraveller& bt) { }; void luckyLottery(int num, BaseTraveller& bt) { }; void unluckyLottery(int num, BaseTraveller& bt) { }; class BaseTraveller { public: typedef void(*pLottery)(int, BaseTraveller&); BaseTraveller(pLottery lty) : _lottery(lty) { } void lottery(int num) { _lottery(num, *this); } private: pLottery _lottery; }; class LittleRMBTraveller : public BaseTraveller { public: LittleRMBTraveller(pLottery lty) : BaseTraveller(lty) { } }; class MuchRMBTraveller : public BaseTraveller { public: MuchRMBTraveller(pLottery lty) : BaseTraveller(lty) { } }; // main.cpp BaseTraveller* pLittleRMB = new LittleRMBTraveller(luckyLottery); pLittleRMB->lottery(10);
在构造函数中传入不同的算法函数,就可以实现不同的行为。
3.藉由std::function实现Strategy模式
与函数指针相比,std::function更具有弹性,它可以接受函数指针,函数对象以及某个类的成员函数,甚至是通过隐式转换而来的以上对象都可以。
struct LuckyLottery { void operator() (int, BaseTraveller&) {} }; class UnluckyBuff { public: void lottery(int, BaseTraveller&) { }; }; class BaseTraveller { public: typedef std::function<void(int, BaseTraveller&)> Lottery; BaseTraveller(Lottery lty) : _lottery(lty) { } void lottery(int num) { _lottery(num, *this); } private: Lottery _lottery; }; // 有些相同部分未列出 // main.cpp BaseTraveller bt(normalLottery); bt.lottery(10); BaseTraveller* plt = new LittleRMBTraveller((LuckyLottery())); plt->lottery(10); UnluckyBuff unlucky; BaseTraveller* plt2 = new MuchRMBTraveller(std::bind(&UnluckyBuff::lottery, unlucky, 10, *plt2)); plt2->lottery(1);
std::function和std::bind需包含头文件<iostream>, <functional>
综上,
- 使用non-virtual interface手法,那是一种Template Method的一种特殊形式。它以public non-virtual成员函数包裹较低访问性的virtual函数;
- 将virtual函数替换为函数指针成员变量,这是Strategy的一种表现手法;
- 以std::function成员变量替换virtual函数,因而允许使用任何可调用物搭配一个兼容于需求的签名式,这也是Strategy的一种形式;
- 将继承体系内的virtual函数替换为另一个继承体系内的virtual函数,这是传统Strategy的一种实现手法。
标签:lottery,Effective,void,35,class,BaseTraveller,virtual,public 来源: https://www.cnblogs.com/strong-monkey/p/16221756.html