Programming 版 (精华区)

发信人: armstrong (小小), 信区: Programming
标  题: C++编程杂谈:面向对象(续) 
发信站: 哈工大紫丁香 (2002年02月20日19:27:37 星期三), 站内信件

 VCKBASE Online Help Journal No.12
C++编程杂谈:面向对象(续)
作者:xulion
下载例子源代码
 
    上一篇我们涉及了面向对象的一个基本概念--封装,封装是一个相对比较简单的概
念,也很容易接受,但是很多的场合下面,仅仅是封装并不能很好的解决很多问题,考
虑下面的例子:
假设我们需要设计一个对战游戏的战斗细节,在最初的版本中我们将支持一种动作--fi
ght。假设我们有三种角色:fighter、knight和warrior,每种角色的health、hit poi
nt不同,基于封装的基本想法,我们很自然的想到对每个对象使用类的封装,首先明显
的元素有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个
不同的类型不是很多,完全可以应付,那么如果有一天系统升级,你需要管理上百种类
型的时候,会不会头大呢?
    在C++中,继承就可以很好的解决这个问题,在C++中,继承表示的是一种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 iHi
tpoint);
    
        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 in
t iHitpoint):
m_iHealth(iHealth),
m_iSpeed(iSpeed),
m_iHitPoint(iHitpoint)
{
    strcpy(m_strName,strName);
}
bool actor::isAlive()
{
    return (m_iHealth>0);
}
/////////////////////////////////////////////////////////
//类fighter
class 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;
    }
}
/////////////////////////////////////////////////////////
//类knight
class 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;
    }
}
/////////////////////////////////////////////////////////
//类warrior
class 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;
    }
}
    C++为我们提供了非常优秀的继承体系(其实这是面向对象的一个非常重要的特征)
,上面的类图中我们可以很清楚的看到他们的关系,在继承中,基类(有些地方也称为
超类)其实是所有派生类的共性提炼的结果,有时候在开发过程中,是先有派生类,然
后再提出共性,产生基类的。
    就象遗传一样,派生类拥有基类的所有属性和方法,如基类actor的成员函数和成员
变量在每一个派生类中都存在,即fighter、knight和warrior中都存在,在继承关系中
还存在一个非常重要的关键字protected,它提供一个界于public和private 之间的一种
可见性,与private相同的地方是对于外部来说,它不可见,与public相同的地方是对与
继承体系来说,是向下可见的(其派生出来的类可以直接使用),这里有一点是很容易
让人迷惑的,就是派生类中基类的成员可见性。对派生类来说,如果使用public继承,
那么从基类中继承的所有成员的可见性不变(如果是protected继承,降一级,public变
成protected,private继承再降),那么对于一个从基类继承来的private成员来说,派
生类是无法访问的(很迷惑,是么?),虽然派生类含有这个变量,但是这是一种间接
的拥有关系,在派生类中,含有一个基类的子对象,派生类对基类成员的访问正是通过
这个子对象进行的。在思想中,永远不要把继承来的成员看做是自己真正拥有的,虽然
使用this可以直接"看到"它们,在派生体系中private和public与其它情况没有任何区别
,而protected在体系的内部就和public完全等同。
    由于派生类拥有基类的成员,所以我们可以通过派生而简单的"重用"我们已有的代
码,从而大大减少重复劳动。
关于继承的另外一个重要的特征就是虚函数,虚函数是形成类的多态的基础,在上面的
类中:
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++;
}
    面向对象思想中的继承是非常重要的概念,正确的运用它,会为软件的开发过程带
来很多便利,同时,COM的核心技术是使用了多重继承来实现的。同时,继承也是面向对
象的思想中较难理解的一个概念,这片文章我只能大致的讲述其运用,而真正的融会贯
通还需要长时间的学习和实践。
    下面我们给出上面的例子的完整代码,这段代码完成了随机建立两个队伍,并进行
战斗,直到有一个队伍全军覆没,最后输出结果。
 
 
 
----------------------------------------------------------------------------
----
?1997-2002 VCKBASE.COM All Rights Reserved.

--
Everybody believes that children are true,
women are beautiful and
men are strong.

※ 来源:·哈工大紫丁香 bbs.hit.edu.cn·[FROM: 202.118.236.209]
[百宝箱] [返回首页] [上级目录] [根目录] [返回顶部] [刷新] [返回]
Powered by KBS BBS 2.0 (http://dev.kcn.cn)
页面执行时间:3.797毫秒