Effective C++ 总结[条款35 考虑virtual函数以外 的其他选择]
2022/5/4 20:13:04
本文主要是介绍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的一种实现手法。
这篇关于Effective C++ 总结[条款35 考虑virtual函数以外 的其他选择]的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!
- 2024-11-23增量更新怎么做?-icode9专业技术文章分享
- 2024-11-23压缩包加密方案有哪些?-icode9专业技术文章分享
- 2024-11-23用shell怎么写一个开机时自动同步远程仓库的代码?-icode9专业技术文章分享
- 2024-11-23webman可以同步自己的仓库吗?-icode9专业技术文章分享
- 2024-11-23在 Webman 中怎么判断是否有某命令进程正在运行?-icode9专业技术文章分享
- 2024-11-23如何重置new Swiper?-icode9专业技术文章分享
- 2024-11-23oss直传有什么好处?-icode9专业技术文章分享
- 2024-11-23如何将oss直传封装成一个组件在其他页面调用时都可以使用?-icode9专业技术文章分享
- 2024-11-23怎么使用laravel 11在代码里获取路由列表?-icode9专业技术文章分享
- 2024-11-22怎么实现ansible playbook 备份代码中命名包含时间戳功能?-icode9专业技术文章分享