【C++】考虑virtual函数以外的其他选择

  假设你正在写一个视频游戏软件,游戏里有各种各样的人物,每个人物都有健康状态,而且不同的人物可能以不同的方式计算他们的健康指数.该如何设计游戏里的人物,主要如何提供一个返回人物健康指数的接口.

  方法一,基于虚函数的方法.

  在人物角色的基类增加一个成员函数heathValue,返回一个整数,表示人物的健康程度,并将声明为virtual.

1 class GameCharacter {
2 public:
3        virtual int healthValue() const;
4        ...
5 };

  heathValue声明为虚函数,因而派生类可以重新定义它,从而获得达到不同的人物可能不同的方式计算他们的健康指数的要求.

  但是没有声明为纯函数,这表示会有个计算健康指数的缺省算法.

  方法二,藉由Non-Virtual Interface手法实现Template Method模式 NVI

  该设计是令客户通过public non-virtual成员函数间接调用private virtual函数,相当对virtual函数进行一层的包装,可以称为是virtual函数的外覆器(warpper).

class GameCharacter {
public:int healthValue() const{...int retVal = doHealthValue();...return retVal;}...
private:virtual int doHealthValue() const{...}
};

  NVI手法的一个优点可以确保在一个virtual函数被调用之前设定好适当的场景,并在调用结束之后清理场景.

  "事前工作"可以包括锁定互斥器,制造运转日志记录项,验证class约束条件,验证函数先决条件等等.

  "事后工作"可以包括互斥器解除锁定,验证函数的事后条件,再次验证class约束条件等等.

  方法三,藉由Function Pointers实现Strategy模式

  每个人物的构造函数接受一个指针,指向一个健康计算函数,调用该函数进行实际计算.

class GameCharacter;
int defaultHealthCalc(const GameCharacter& gc);
class GameCharacter {
public:typedef int (*HealthCalcFunc)(const GameCharacter&);explicit GameCharacter(HealthCalcFunc hcf = defaultHealthCalc):healthFunc(hcf){}int healthValue() const{return healthFunc(*this);}...
private:HealthCalcFunc healthFunc;
};

  该方法的优点,同一人物类型之不同实体可以有不同的健康计算函数,只需要在构造实例时,传入不同的计算函数的指针.

  某已知人物之健康计算函数可在运动期变更,可以在GameCharacter里提供一个成员函数setHealthCalc,用来替换当前的健康指数计算函数.

  该方法的缺点,如果需要利用GameCharacter的non-public信息进行计算健康指数时,由于计算函数是non-member non-friend函数,将出现无法访问的问题.

  如果让计算函数访问成功,则需要降低GameCharacter的封装性.

  方法四,藉由tr1::function完成Strategy模式

  不再使用函数指针,而是改用一个类型为tr1::function的对象.

  可以是函数指针,函数对象,或成员函数指针,只要其签名式兼容于需求端.

View Code
class GameCharacter;
int defaultHealthCalc(const GameCharacter& gc);
class GameCharacter {
public:typedef    std::tr1::function<int (const GameCharacter&)> HealthCalcFunc;explicit GameCharacter(HealthCalcFunc hcf = defaultHealthCalc):healthFunc(hcf){}int healthValue() const{return healthFunc(*this);}...
private:HealthCalcFunc healthFunc;
};short calcHealth(const GameCharacter&);
struct HealthCalculator {int operator()(const GameCharacter&) const{...}
};
class GameLevel {
public:float health(const GameCharacter&) const;...
};
class EvilBadGuy:public GameCharacter{
...
};
EvilBadGuy ebg1(calcHealth);            //函数
EvilBadGuy ebg2(HealthCalculator());    //函数对象
GameLevel currentLevel;
EvilBadGuy ebg3(std::tr1::bind(&GameLevel::health,currentLevel,_1);    //成员函数

  优点,以tr1::function替换函数指针之后,可以允许客户在计算人物健康指数时使用任何兼容的可调用物.

  方法五,古典的Strategy模式

  将健康计算函数做成一个分离的继承体系中的virtual成员函数.

  每个GameCharacter对象都内含一个指针,指向一个来自HealthCalcFunc继承体系的对象

 

class GameCharacter;
class HealthCalcFunc {
public:...virtual int calc(const GameCharacter& gc) const{...}...
};
HealthCalcFunc defaultHealthCalc;
class GameCharacter {
public:explicit GameCharacter(HealthCalcFunc* phcf = &defaultHealthCalc):pHealthFunc(phcf){}int healthValue() const{return pHealthFunc->calc(*this);}...
private:HealthCalcFunc* pHealthFunc;
};

  优点,只要为HealthCalcFunc继承体系添加一个派生类,就可以将一个既有的健康算法纳入使用.

  启发:针对具体的应用问题,需要认真分析其应用的特点,以及应用的后续扩展等问题,再从众多的方法,选取最合适的方法.

  不能先入为主的,随便的套用一个方法,这样可能会导致应用的后续扩展问题.总之,遇到问题,先想,再比较,最后确定方案.

  参考资料:Effective C++中文版

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/463572.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

不知道你们遇到这样的问题没?

最近在网上看到这样一个内容https://developer.horizon.ai/forumDetail/118363914936419003关于J5/J3/J2平台的底层软件地平线内部的释放计划和形式&#xff1f;您好&#xff1a;问题如题&#xff0c;我们当前在地平线J5平台展开进行的项目居多&#xff0c;跟贵司接触和合作的部…

boost::split()的使用方式

引用的头文件 <boost/algorithm/string.hpp> boost::split()函数用于切割string字符串&#xff0c;将切割之后的字符串放到一个std::vector<std::string> 之中&#xff1b; 有4个参数&#xff1a; 以boost::split(type, select_list, boost::is_any_of(",&…

第四周:机器学习知识点回顾

前言&#xff1a; 讲真&#xff0c;复习这块我是比较头大的&#xff0c;之前的线代、高数、概率论、西瓜书、樱花书、NG的系列课程、李宏毅李沐等等等等…那可是花了三年学习佳实践下来的&#xff0c;现在一想脑子里就剩下几个名词就觉得废柴一个了&#xff0c;朋友们有没有同感…

移植linux内核到i.MX6ULL过程

本文描述移植NXP官方 linux 5.4 内核到i.MX6ULL开发板。一、NXP官方linux内核1. 下载 NXP官方linux仓库地址为&#xff1a;https://github.com/Freescale/linux-fslc/tree/5.4-2.1.x-imx。选择该分支下载zip包即可&#xff0c;不要整个仓库下载&#xff0c;太大了&#xff1a;2…

Go语言之进阶篇http服务器获取客户端的一些信息

1、http服务器获取客户端的一些信息 示例: package mainimport ("fmt""net/http" )//w, 给客户端回复数据 //r, 读取客户端发送的数据 func HandConn(w http.ResponseWriter, r *http.Request) {fmt.Println("r.Method ", r.Method)fmt.Println…

R学习之——R用于文本挖掘(tm包)

首先需要安装并加载tm包。 1、读取文本 x readLines("222.txt") 2、建立语料库 > rCorpus(VectorSource(x))> rA corpus with 7012 text documents 3、语料库输出&#xff0c;保存到硬盘 > writeCorpus(r) 4、查看语料库 > print(r) A corpus with 7012…

学了STM32要继续学习Linux吗?

关注我的读者中&#xff0c;有很大一部分是单片机&#xff08;STM32&#xff09;的开发者&#xff0c;经常看到有人问类似的问题&#xff1a;学了STM32要继续学习Linux吗&#xff1f;每个人的情况不同&#xff0c;到底要不要学习Linux&#xff0c;要结合自身的情况。有的人已经…

模板函数与特化函数

本文转自&#xff1a;https://www.cnblogs.com/dracohan/p/3401660.html 转来收藏以便查阅&#xff0c;感谢原作者 今天在写代码时&#xff0c;遇到了模板和特化&#xff0c;在网上找了资料后问题呗一一解决&#xff0c;转载此文用于以后查阅&#xff0c;感谢原创者。其中增加…

这样调试内核启动流程

内核生命周期uboot 打印完 Starting kernel . . .&#xff0c;就完成了自己的使命&#xff0c;控制权便交给了 kernel 的第一条指令&#xff0c;也就是下面这个函数init/main.casmlinkage __visible void __init start_kernel(void){...rest_init();}start_kernel 相当于内核的…

ios 图片添加阴影

2019独角兽企业重金招聘Python工程师标准>>> UIimageView *imageView [[UIImageView alloc ] init]; imageView.layer.shadowColor [UIColor blackColor].CGColor; imageView.layer.shadowOffset CGSizeMake(3,2); imageView.layer.shadowOpacity 0.6; imageVie…

asp.net定时执行任务-解决应用池回收问题----转载

在复杂的业务应用程序中&#xff0c;有时候会要求一个或者多个任务在一定的时间或者一定的时间间隔内计划进行&#xff0c;比如定时备份或同步数据库&#xff0c;定时发送电子邮件&#xff0c;定期处理用户状态信息&#xff0c;支付系统中定期同步异常账单等等&#xff0c;我们…

bool与string互转

今天在工作中遇到了将string转换成bool类型数据&#xff0c;查阅了工具书解决了问题&#xff0c;现将注意要点总结如下&#xff1a; 增加头文件&#xff1a;#include <sstream> 代码如下&#xff1a; 在codeblocks软件上测试结果如下&#xff1a; 使用者需要根据自己的实…

Go语言之高级篇beego框架之参数配置与路由配置

一、参数配置 beego默认会解析当前应用下的conf/app.conf文件 1.1、beego的参数配置 appname WEB httpport 8080 runmode dev 几种开发模式 [dev] httpprot 8080 [prod] httpport 8081 [test] httpport 8082 //备注&#xff1a; beego.AppConfig.String( "dev::m…

C++ int转string以及源码

今天遇到一个int类型数据转换为string&#xff0c;查了资料在c11标准中增加了全局函数std::to_string来实现该功能&#xff1a; string to_string (int val); string to_string (long val); string to_string (long long val); string to_string (unsigned val); string t…

EUREKA原理总结

Eureka高可用架构 https://github.com/Netflix/eureka/wiki/Eureka-at-a-glance 上图中主要的名称说明&#xff1a; Register&#xff1a;EurekaClient注册&#xff08;Http请求&#xff09;到EurekaServer&#xff0c;EurekaClient会发送自己元数据(ip,port,主页等)&#xff0…

linux下安装oracle 11g R2

Linux环境配置 [c-sharp]view plaincopy OS:Fedora 15 DB:Oracle 11gR2 将Oracle安装到home/oracle_11目录 配置过程&#xff1a;本文来自Oracle官方文档网上资料 Oracle官方文档&#xff1a;http://www.oracle.com/pls/db112/homepage 1. 以root用户登录到Linux 2. 检查机器…

通俗理解数字签名,ssl数字证书和https

前言 最近在开发关于PDF合同文档电子签章的功能&#xff0c;大概意思就是在一份PDF合同上签名&#xff0c;盖章&#xff0c;使其具有法律效应。签章有法律效应必须满足两个条件&#xff1a; 能够证明签名&#xff0c;盖章者是谁&#xff0c;无法抵赖PDF合同在签章后不能被更改在…

linux 性能分析工具——perf

最近需要对linux下的开发的数据库应用程序进行性能调试&#xff0c;找到了该篇文章&#xff0c;保存下来为了以后便于查找&#xff0c;这篇是转载的perf文章&#xff0c;后续还有vtune相关的文章。 转载&#xff1a;https://blog.csdn.net/u014608280/article/details/8026571…

红外遥控

红外遥控简介红外遥控是一种无线、非接触控制技术,具有抗干扰能力强,信息传输可靠,功耗低,成本低,易实现等显著优点,被诸多电子设备特别是家用电器广泛采用,并越来越多的应用到计算机系统中。由于红外线遥控不具有像无线电遥控那样穿过障碍物去控制被控对象的能力&#xff0c;所…

使用jQuery Mobile移动开发框架将博客网站快速转化为Mobile网站

日期&#xff1a;2012-7-12 来源&#xff1a;GBin1.com 在线演示 jQuery Mobile是一个非常不错的移动端网站应用的解决方案&#xff0c;很多网站都使用jQuery Mobile来生成Mobile手机端的移动网站应用&#xff0c;在过去的GBin1博客文章中&#xff0c;我们曾经使用jQuery Mob…