《面向对象程序设计》自学方法指导
来源: 发布时间:2006-5-29 17:04:30 点击:
 
  (1)通过友员来继承,可以把整个派生类或派生类中的某些方法声明为基类的友员,这样就可以访问基类中的私有部分。
  (2)利用类定义中的保护部分(protected)。在基类中声明为protected的类成员,可以被派生类继承。
我们先举一个简单的例子来说明继承和派生的关系,然后再详细说明访问属性与控制之间的关系。
  //定义一个枚举
  enum BugColor{red,green,blue,yelloe,black};
  class Bug //定义一个昆虫基类
  { protected:
  int legs;
  BugColor color;
  public:
  //成员函数
  Bug(int numlegs,BugColor c);
  void Draw();
  };
  //定义一个派生类
  class HumBug:public Bug
  {
  private:
  int Frenquency;
  public:
  HumBug(int numlegs, BugColor c,int Fren);
  void Hum();
  };
  该例把类HumBug定义为基类Bug的派生类,由于访问属性为public,因此基类Bug中的所有Public成员也是派生类HumBug中的public成员,而基类中的保护成员也是派生类中的保护成员。虽然HumBug定义了自己的方法,但它也继承了基类Bug中定义的方法。因此下面的函数是合法的:
  void func()
  {
  Bug b(8,blue);
  HumBug h(10,green,1000);
  b.Draw();
  h.Draw();
  h.Hum();
  }
  上例定义了基类Bug的派生类HumBug。实际上,还可以以HumBug为基类,再定义派生类,从而形成类层次结构。当访问属性使用不同的访问说明符时,派生类从基类中继承的访问控制也不一样。上面的例子从基类Bug产生出派生类HumBug,继承了基类中的protected和public成员,形成如图4.1所示的类层次,其中划底线数据和方法是从基类继承来的。




            




            



              图4-1
  访问属性与控制:
  综上所述,可以看出,派生类继承了基类中的部分成员。类中的数据成员和成员函数有的可以被类本身存取,有的可以被该类的派生类存取;有的可以被类本身的对象存取,有的则可以被派生类的对象存取。这种访问控制由访问说明符来决定,包括类定义中使用的访问说明符和定义派生类时使用的访问说明符。例如,定义为私有(private)的数据成员或函数只能被类本身的成员存取;而定义为公有(public)的数据成员或函数可以被派生类的成员或派生类的对象存取。见表4-2:

类成员类型 类本身 类对象 派生类 派生类对象
private   允许   不允许 不允许 不允许
public    允许   允许   允许   允许
protected 允许   不允许 允许   不允许

  表4-2 基类和派生类的访问控制
  从上面的表中可以看出:
  (1) 类定义本身可以存取声明为protected、public、private类型的数据成员或成员函数。也就是说,在类定义内部,可以存取任何成员。
  (2) 一个类的对象只能存取在该类或其基类中声明为public类型的数据成员或函数。
  (3) 派生类只能存取在基类中声明为protected、public类型的数据成员或成员函数。
  (4) 派生类的对象只能存取在基类中声明为public的数据成员或成员函数。
  注意,上面所说的访问控制只是"可以",不是"必定"。因为它还要看在定义派生类时所使用的访问属性。
  根据访问属性的不同,从理论上说,派生类和基类的继承关系可以有三种类型,即公有、保护和私有继承类型。但在实际使用时,-般只有两种类型。则公有和私有类型。这是因为保护继承类型所继承的成员为私有或保护成员。不能在类定义之外访问,这样的派生类是没有意义。因此通常只使用以下两种继承类型:公有继承类型和私有继承类型。
  7.多态性与虚函数
  (1)多态性
  面向对象程序设计有三个重要的特色:数据封装、继承和多态性。前两个特性我们已给大家作了详细的讲解,下面主要讲多态性。
用多态性可以实现自上而下的设计方法。这是-种从全局出发,用类的层次结构来模拟客观世界的程序设计力法。通俗地说,多态性是指用一个相同的名字定义不同的函数,这些函数执行不同但又类似的操作,即用同样的接口访问功能不同的函数。前面讲过的函数重载就是一种多态性,这是编译时的多态性,也称静态多态性。多态性还有一种实现方法,就是动态多态性,相对与静态多态性,它的实现代码是在运行是选定的,并且提供了更好的灵活性、问题的抽象性和程序的易维护性,但是它的运行效率相对来说较低。
  面向对象的程序设计语言支持多态性。从本质上说,多态性可以引用多个类的实例。利用多态性,程序员可以向一个对象发送消息来完成一系列操作,而不必关心软件是怎样实观这些操作的。在系统设计阶段.当设汁人员决定把某一类型的活动用于一个给定的对象时,并不关心这个对象如何解释这个活动(消息)以及这个方法如何实现,而只关心这个活动对这个对象所产生的作用,C++允许程序员向不同但有关的对象发送同样的消息和完成同样的操作.而让软件系统决定如何为给定的对象完成所需要的操作。
  多态性具有两方面的作用。首先.它可以用-种相似的方式来处理相关的概念;其次,多态性也使得程序更易于扩充.当增加一种与已有类相关的类时,利用多态性无需修改程序的其余部分。
  (2)虚函数
  虚函数是在基类中用关键字virtual来声明需要重载的函数,关键字只能在基类中用,在派生类中就不能在重载的函数前面加上virtual了。
  虚函数只需简单的加上一个关键字virtual就可以了,但是为什么要使用虚函数呢?这一点参考上并没有讲清楚,不了解使用虚函数的意义,就不能更深层的了解虚函数。所以在这里有必要跟大家说一说。
  先看一个例子:
  class classbase
  {
  public:
  void afn(void)
  { cout<<〃here is base class〃<<endl;}
  };
  class classderived:classbase
  {
  public:
  void afn(void)
  { cout<<〃here is derived class〃<<endl;}
  };
  void fn(classbase * pclassbase)
  {
  pclassbase->afn();
  }
  在执行函数fn()时,两个类的成员函数afn()都有可能被调用。因为C++的继承采用了复制继承的方式。也就是说,派生类的对象要为基类的所有成员分配空间(成员函数则分配函数指针空间),将它们存放在此空间内,然后再存放派生类新增的成员。类classbase和类classderived在内存的存放布局为下图所示:






  从图中可以看出,pclassbase->afn()应该调用的是第一个afn(),即classbase::afn(),如果要调用classderived::afn()必须显式指出,这实际上是"一个接口,一种方法",没有多态性。在继承机制下,编译程序有时难以决定调用哪一个重载函数,只有在运行时才能确定.这就是滞后联编。用滞后联编可以实现多态性。
  还是看上面的一个例子,我们只在基类classbase的定义中,在成员函数afn()的前面加上一个关键字:virtual。别的声明都不变。下面是主函数main();
  int main()
  {
  classbase objb;
  classderived objd;
  cout<<〃first call:〃afn(&objb);
  cout<<〃second call:〃<<afn(&objd);
  return 0;
  }
  在该程序中,函数afn被调用了两次,第一次调用时传递classbase指针,第二次调用时传递classderived指针,程序执行结果如下:
  first call: here is base;
  second call: here is derived;
  这就是说,通过改变指针参数,可以使同一个函数afn分别调用两个类中的成员函数,从而实现了"一个接口,两种方法",这就是多态性。

本新闻共7页,当前在第5页  1  2  3  4  5  6  7