17xie > VC++深入详解 > 2.2.6 类的继承
背景:                 
[本书目录] [图书首页] [本书讨论区]  
链接地址:http://www.17xie.com/read-36292.html    注册17xie 一起来写书 实现您的出书梦想!

2.2.6  类的继承

1.继承

我们定义一个动物类,对于动物来说,它应该具有吃、睡觉和呼吸的方法。

class animal

{

public:

    void eat()

    {

        cout<<"animal eat"<<endl;

    }

    void sleep()

    {

        cout<<"animal sleep"<<endl;

    }

    void breathe()

    {

        cout<<"animal breathe"<<endl;

    }

};

我们再定义一个鱼类,对于鱼来说,它也应该具有吃、睡觉和呼吸的方法。

class fish

{

public:

     void eat()

     {

          cout<<"fish eat"<<endl;

     }

     void sleep()

     {

          cout<<"fish sleep"<<endl;

     }

     void breathe()

     {

          cout<<"fish breathe"<<endl;

     }

};

如果我们再定义一个绵羊类,对于绵羊来说,它也具有吃、睡觉和呼吸的方法,我们是否又重写一遍代码呢?既然鱼和绵羊都是动物,是否可以让鱼和绵羊继承动物的方法呢?C++中,提供了一种重要的机制,就是继承。类是可以继承的,我们可以基于animal这个类来创建fish类,animal称为基类(Base Class,也称为父类),fish称为派生类(Derived Class,也称为子类)。派生类除了自己的成员变量和成员方法外,还可以继承基类的成员变量和成员方法。

重写animalfish类,让fishanimal继承,代码如例2-11所示(EX05.CPP)。

2-11

#include <iostream.h>

class animal

{

public:

     void eat()

     {

          cout<<"animal eat"<<endl;

     }

     void sleep()

     {

          cout<<"animal sleep"<<endl;

     }

     void breathe()

     {

          cout<<"animal breathe"<<endl;

     }

};

class fish:public animal

{  

};

void main()

{

     animal an;

     fish fh;

     an.eat();

     fh.eat();

}

虽然fish类没有显式地编写一个方法,但fishanimal已经继承eatsleepbreathe方法,我们通过编译运行可以看到结果。

下面,我们在animal类和fish类中分别添加构造函数和析构函数,然后在main函数中定义一个fish类的对象fh,看看在构造fish类的对象时,animal类的构造函数是否被调用;如果调用,animal类和fish类的构造函数的调用顺序是怎样的。完整代码如例2-12所示(EX06.CPP)。

2-12

#include <iostream.h>

class animal

{

public:

     animal()

     {

          cout<<"animal construct"<<endl;

     }

     ~animal()

     {

          cout<<"animal destruct"<<endl;

     }

     void eat()

     {

          cout<<"animal eat"<<endl;

     }

     void sleep()

     {

          cout<<"animal sleep"<<endl;

     }

     void breathe()

     {

          cout<<"animal breathe"<<endl;

     }

};

class fish:public animal

{

public:

     fish()

     {

          cout<<"fish construct"<<endl;

     }

     ~fish()

     {

          cout<<"fish destruct"<<endl;

     }

};

void main()

{

     fish fh;

}

编译运行,出现如图2.11所示的结果。

可以看到当构造fish类的对象fh时,animal类的构造函数也要被调用,而且在fish类的构造函数调用之前被调用。当然,这也很好理解,没有父亲就没有孩子,因为fish类从animal类继承而来,所以在fish类的对象构造之前,animal类的对象要先构造。在析构时,正好相反。

2.11  EX06.CPP程序的运行结果

2.在子类中调用父类的带参数的构造函数

下面我们修改一下animal类的构造函数,增加两个参数heightweight,分别表示动物的高度和重量。代码如例2-13所示。

2-13

#include <iostream.h>

class animal

{

public:

     animal(int height, int weight)

     {

         cout<<"animal construct"<<endl;

     }

     ~animal()

     {

         cout<<"animal destruct"<<endl;

     }

     void eat()

     {

         cout<<"animal eat"<<endl;

     }

     void sleep()

     {

         cout<<"animal sleep"<<endl;

     }

     void breathe()

     {

         cout<<"animal breathe"<<endl;

     }

};

class fish:public animal

{

public:

     fish()

     {

         cout<<"fish construct"<<endl;

     }

     ~fish()

     {

         cout<<"fish destruct"<<endl;

     } 

};

void main()

{

     fish fh;

}

当我们编译这个程序时,就会出现如下错误:

那么这个错误是如何出现的呢?当我们构造fish类的对象fh时,它需要先构造animal类的对象,调用animal类的默认构造函数(即不带参数的构造函数),而在我们的程序中,animal类只有一个带参数的构造函数,在编译时,因找不到animal类的默认构造函数而出错。

因此,在构造fish类的对象时(调用fish类的构造函数时),要想办法去调用animal类的带参数的构造函数,那么,我们如何在子类中向父类的构造函数传递参数呢?可以采用如例2-14所示的方式,在构造子类时,显式地去调用父类的带参数的构造函数。

2-14

#include <iostream.h>

class animal

{

public:

    animal(int height, int weight)

    {

        cout<<"animal construct"<<endl;

    }

    …

};

class fish:public animal

{

public:

    fish():animal(400,300)

    {

        cout<<"fish construct"<<endl;

    }

    …

};

void main()

{

    fish fh;

}

注意程序中以粗体显示的代码。在fish类的构造函数后,加一个冒号(:),然后加上父类的带参数的构造函数。这样,在子类的构造函数被调用时,系统就会去调用父类的带参数的构造函数去构造对象。这种初始化方式,还常用来对类中的常量(const)成员进行初始化,如下面的代码所示:

class point

{

public:

     point():x(0),y(0)

private:

     const int x;

     const int y;

};

当然,类中普通的成员变量也可以采取此种方式进行初始化,然而,这就没有必要了。

3.类的继承及类中成员的访问特性

在类中还有另外一种成员访问权限修饰符:protected。下面是publicprotectedprivate三种访问权限的比较:

n  public定义的成员可以在任何地方被访问。

n  protected定义的成员只能在该类及其子类中访问。

n  private定义的成员只能在该类自身中访问。

对于继承,也可以有publicprotectedprivate这三种访问权限去继承基类中的成员,例如,例2-14所示代码中,fish类继承animal类,就是采用public的继承方式。如果在定义派生类时没有指定如何继承访问权限,则默认为private。如果派生类以private访问权限继承基类,则基类中的成员在派生类中都变成了private类型的访问权限。如果派生类以public访问权限继承基类,则基类中的成员在派生类中仍以原来的访问权限在派生类中出现。如果派生类以protected访问权限继承基类,则基类中的publicprotected成员在派生类中都变成了protected类型的访问权限。

注意:基类中的private成员不能被派生类访问,因此,private成员不能被派生类所继承。

4.多重继承

如同该名字中所描述的,一个类可以从多个基类中派生。在派生类由多个基类派生的多重继承模式中,基类是用基类表语法成分来说明的,多重继承的语法与单一继承很类似,只需要在声明继承的多个类之间加上逗号来分隔,定义形式为:

class派生类名:访问权限 基类名称,访问权限 基类名称,访问权限 基类名称

{

       ……

};

例如B类是由类C和类D派生的,可按如下方式进行说明:

class Bpublic C, public D

{

       ……

}

基类的说明顺序一般没有重要的意义,除非在某些情况下要调用构造函数和析构函数时,在这样的情况下,会有一些影响。

n 由构造函数引起的初始化发生的顺序。如果你的代码依赖于BD部分要在C部分之前初始化,则此说明顺序将很重要,你可以在继承表中把D类放到C类的前面。初始化是按基类表中的说明顺序进行初始化的。

n 激活析构函数以做清除工作的顺序。同样,当类的其他部分正在被清除时,如果某些特别部分要保留,则该顺序也很重要。析构函数的调用是按基类表说明顺序的反向进行调用的。

虽然,多重继承使程序编写更具有灵活性,并且更能真实地反映现实生活,但由此带来的麻烦也不小。我们看例2-15所示的程序(EX07.CPP):

2-15

1 #include <iostream.h>

2 class B1

3 {

4 public:

5     void output();

6 };

7 class B2

8 {

9 public:

10     void output();

11};

12void B1::output()

13{

14     cout<<"call the class B1"<<endl;

15}

16void B2::output()

17{

18     cout<<"call the class B2"<<endl;

19}

20

21class A:public B1,public B2

22{

23public:

24     void show();

25};

26void A::show()

27{

28     cout<<"call the class A"<<endl;

29}

30void main()

31{

32     A a;

        a.output();         //该语句编译时会报错

33     a.show();

34}

2-15的程序乍一看,好像没有错误,但是,编译时就会出错。原因何在?由第21行代码我们知道派生类A是从基类B1B2多重继承而来的,而基类B1B2各有一个output()函数,在第33行,当类A的对象a要使用a.output()时,编译器无法确定用户需要的到底是哪一个基类的output()函数,而产生'A::output' is ambiguous的错误信息,请读者注意。


字数:8939    最后更新:8个月以前 [03-13 21:30]happyskynet 修改
本页编辑者:happyskynet  
[前一页]:2.2.5 this指针  [后一页]:2.2.7 虚函数与多态性…
[在本页中加入书签] [收藏本书] [推荐本书]
  17xie论坛 > 本书讨论区 > 本页评论   (共0条)
发表评论

用户名称 匿名发表
评论内容
验证码

关于我们 | 版权声明 | 免责声明 | 诚聘英才 | 联系我们 | 合作伙伴 | 友情链接 | 广告合作 | 提交意见
Copyright © 2007 17xie.com 互联网协同写书平台 京ICP备08002671号