Make Www.Edu-cn.Com Your Home Page!
用户名: 密码: 验证码: 注册
  当前位置: > 中国教程网>软件开发>vc> C++编程杂谈之三:面向对象(续)

C++编程杂谈之三:面向对象(续)

作者:   来源:中国教程网   点击:   日期:2007-04-02

编程杂谈之三:面向对象(续)

作者:xulion

下载例子源代码

  edu4u.com.cn

    上一篇我们涉及了面向对象的一个基本概念--封装,封装是一个相对比较简单的概念,也很容易接受,但是很多的场合下面,仅仅是封装并不能很好的解决很多问题,考虑下面的例子:
假设我们需要设计一个对战游戏的战斗细节,在最初的版本中我们将支持一种动作--fight。假设我们有三种角色:fighter、knight和warrior,每种角色的health、hit point不同,基于封装的基本想法,我们很自然的想到对每个对象使用类的封装,首先明显的元素有2个:health、hit point,另外还有name(我们不能只有三个角色)和战斗的速度speed,方法有:hit、isalive。基于这样的想法,我们给出下面的类:
 
 class fighter{	private:		int               m_iHealth;		int              m_iSpeed;//为了方便,这个值使用延时值,越大表示速度越慢		const int          m_iHitPoint;		char             m_strName[260];	public:		fighter(const char* strName);		void hit(fighter* pfighter);		bool isAlive(); };     中国论文网 
    上面的类可以清楚的抽象出我们所需要表达的数据类型之一,这里也许你已经发现了一些问题:
成员函数hit用来处理战斗事件,但是我们有不同的角色,fighter不可能只跟自己同样的对手作战,我们希望它能和任何角色战斗,当然,使用模板函数可以简单的解决这个问题。另外,我们必须去实现三个不同的类,并且这些类必须都实现这些属性和方法。即使这些问题我们都解决了,现在我们要组织两个队伍作战,我们希望使用一种群体类型来描述它们,问题是我们必须针对每一种类建立相应的群体结构,当然你可以认为3个不同的类型不是很多,完全可以应付,那么如果有一天系统升级,你需要管理上百种类型的时候,会不会头大呢?
    在中,继承就可以很好的解决这个问题,在中,继承表示的是一种IS-A关系,即派生类IS-A基类,很多现实世界中的关系可以这样来描述,如:dog is-a animal,dog是animal的派生(继承),继承产生的对象拥有父(基)对象的所有属性和行为,如animal的所有属性和行为在dog身上都会有相应的表现。在UML的描述中,这种关系被称为泛化(generalization)。一般情况下,当我们需要实现一系列相似(具有一定的共性)然而有彼此不同的类别的时候,使用继承都是很好的解决办法,例如前面的代码虽然也能够实现我们的目标,但是显然很难管理,下面给出使用继承后的实现:
 class actor//基类{	protected:		int               m_iHealth;		const int         m_iSpeed;//为了方便,这个值使用延时值,越大表示速度越慢		const int         m_iHitPoint;		char              m_strName[260];	public:		actor(const char* strName,const int iHealth,const int iSpeed,const int iHitpoint);			int& Health(){ return m_iHealth; };		const char* getName(){ return m_strName; };		virtual void hit(actor *Actor) = 0;		bool isAlive(); };  actor::actor(const char* strName,const int iHealth,const int iSpeed,const int iHitpoint):m_iHealth(iHealth),m_iSpeed(iSpeed),m_iHitPoint(iHitpoint){	strcpy(m_strName,strName);}bool actor::isAlive(){	return (m_iHealth>0);}///////////////////////////////////////////////////////////类fighterclass fighter :public actor{	public:		fighter(const char* strName);		virtual void hit(actor *Actor);	private:};fighter::fighter(const char* strName):actor(strName,100,20,20){}void fighter::hit(actor *Actor){	Sleep(m_iSpeed);	if(!isAlive())	{		return ;	}	if(Actor&&Actor->isAlive())	{	 	//这里我们用一个函数做了左值,因为函数返回的是引用		if(isAlive())			Actor->Health() = Actor->Health() - m_iHitPoint;	}}///////////////////////////////////////////////////////////类knightclass knight :public actor{	public:		knight(const char* strName);		virtual void hit(actor *Actor);	private:};knight::knight(const char* strName):actor(strName,150,20,25){}void knight::hit(actor *Actor){	Sleep(m_iSpeed);	if(!isAlive())	{		return ;	}	if(Actor&&Actor->isAlive())	{		if(isAlive())			Actor->Health() = Actor->Health() - m_iHitPoint;	}}///////////////////////////////////////////////////////////类warriorclass warrior :public actor{	public:		warrior(const char* strName);		virtual void hit(actor *Actor);	private:};warrior::warrior(const char* strName):actor(strName,150,20,25){}void warrior::hit(actor *Actor){	Sleep(m_iSpeed);	if(!isAlive())	{		return ;	}	if(Actor&&Actor->isAlive())	{		if(isAlive())			Actor->Health() = Actor->Health() - m_iHitPoint;	}} AOE3 STUDIO 
    为我们提供了非常优秀的继承体系(其实这是面向对象的一个非常重要的特征),上面的类图中我们可以很清楚的看到他们的关系,在继承中,基类(有些地方也称为超类)其实是所有派生类的共性提炼的结果,有时候在开发过程中,是先有派生类,然后再提出共性,产生基类的。
    就象遗传一样,派生类拥有基类的所有属性和方法,如基类actor的成员函数和成员变量在每一个派生类中都存在,即fighter、knight和warrior中都存在,在继承关系中还存在一个非常重要的关键字protected,它提供一个界于public和private 之间的一种可见性,与private相同的地方是对于外部来说,它不可见,与public相同的地方是对与继承体系来说,是向下可见的(其派生出来的类可以直接使用),这里有一点是很容易让人迷惑的,就是派生类中基类的成员可见性。对派生类来说,如果使用public继承,那么从基类中继承的所有成员的可见性不变(如果是protected继承,降一级,public变成protected,private继承再降),那么对于一个从基类继承来的private成员来说,派生类是无法访问的(很迷惑,是么?),虽然派生类含有这个变量,但是这是一种间接的拥有关系,在派生类中,含有一个基类的子对象,派生类对基类成员的访问正是通过这个子对象进行的。在思想中,永远不要把继承来的成员看做是自己真正拥有的,虽然使用this可以直接"看到"它们,在派生体系中private和public与其它情况没有任何区别,而protected在体系的内部就和public完全等同。 edu4u.com.cn
    由于派生类拥有基类的成员,所以我们可以通过派生而简单的"重用"我们已有的代码,从而大大减少重复劳动。
关于继承的另外一个重要的特征就是虚函数,虚函数是形成类的多态的基础,在上面的类中:
void knight::hit(actor *Actor)
比如下面的伪码:
 actor* pA;knight* pKnight = new knight;pA = pKnight;pA->hit(…); 中国教程网 
    这里pA是一个基类的指针,而它指向的是一个派生类knight的指针,调用它应该是怎么样的情况呢?答案是hit会调用knight的方法,而不是基类的,因为它是一个虚函数,虚函数的特点是它永远忠实与实际的对象(前提是正确的使用),通过这种多态的特性,我们可以使用基类来对派生类进行正确的操作,这就解决了一个上面的问题:使用一种群体类型来描述它们,所以我们可以这样来遍历数据,而不需要关心里面究竟是一些什么样的数据
 vector troop1;vector troop2;vector::iterator it_tp1 = troop1.begin();vector::iterator it_tp2 = troop2.begin();while( (it_tp1!=troop1.end()) && (it_tp2!=troop2.end()) ){	while(((*it_tp1)->isAlive())&&(it_tp2!=troop2.end()))	{		(*it_tp1)->hit(*it_tp2);		if(!(*it_tp2)->isAlive())			it_tp2++;	}	it_tp1++;} www.edu-cn.com 
    面向对象思想中的继承是非常重要的概念,正确的运用它,会为软件的开发过程带来很多便利,同时,COM的核心技术是使用了多重继承来实现的。同时,继承也是面向对象的思想中较难理解的一个概念,这片文章我只能大致的讲述其运用,而真正的融会贯通还需要长时间的学习和实践。
    下面我们给出上面的例子的完整代码,这段代码完成了随机建立两个队伍,并进行战斗,直到有一个队伍全军覆没,最后输出结果。

www.edu4u.com.cn



文章评论】 【收藏本文】 【推荐好友】 【打印本文】 【论坛讨论

   最新文章:
·static_cast揭密(04-02)
·泛型编程-转移构造函数(Generic Prog(04-02)
·使用MFC的数组类(04-02)
·浅析VC与MATLAB联合编程(04-02)
·精通VC与MATLAB联合编程——综合举例(04-02)
·精通VC与Matlab联合编程(五)(04-02)
·精通 VC 与 MATLAB 联合编程——综合(04-02)
·在VC资源文件中加入声音资源(04-02)
·const传奇(04-02)
·初始化类对象(04-02)
   相关文章:
·初始化类对象 ·C++编程杂谈之二:面向对象
·单实例设计模式的实现 ·C++编程杂谈之一:编译器
·为什么operator=操作符返回引用 ·解析动态联编(上篇) - 作者:tingya
·自制性能测试类 ·解析动态联编(下篇) - 作者:tingya
·在名字空间中声明类和成员函数 ·创建和访问环境变量

   文章评论:(0条)
  
用户名: 新注册) 密码: 匿名评论
评论内容:(不能超过250字,需审核后才会公布,请自觉遵守互联网相关政策法规。

 §最新评论:

  责任编辑:中国教程网  

© 2007 Copyright 中国教育网
DesignBy:AOE3 STUDIO & ROCOCO