您好,欢迎访问三七文档
当前位置:首页 > 商业/管理/HR > 其它文档 > 第五章 类的继承与派生
第5章类的继承与派生•达尔文的进化论表明每个物种都在产生具有相似特征的后代。我们所说的类的继承(inheritance)也与此类似,当然没有生物界那样复杂与深奥。•继承性使用户可以连续建立或扩展自己或他人所建立的类而不受什么限制。从最简单的类开始,可以派生出越来越复杂的类。这既易于管理和跟踪,又使类本身变得很简单。当然这也得益于类的封装性。•可复用性是面向对象的程序设计中的常见术语,它指的是对一个类来说,可以在程序中直接对其实例进行操作、使用,或者以其作为基类产生部分或全部性质的子类。通过从基类中派生其他类,可有效地根据需要重复使用基类代码。。•在继承关系中,通常被继承的类称为基础类或父类,而经由继承产生的类,则称为子类或派生类。•继承关系是一种is-a关系,利用现有概念来定义一个新的概念。C++Builder可视组件的层次关系TObjectExceptionTstreamTpersistentTprinterTlistTgraphicobjectTgraphicTcomponentTcanvastpicturetStringsTtimerTscreenTmenuitemTcontrolTcommondialogTgloblcomponentTgraphicControlTwinControlTcustomComboBoxTButtonControlTcustomControlTscrollBarTcustomEditTscrollingWinControlTcustomListBoxTForm1.类继承关系的语法形式:class派生类名:访问控制基类名1,访问控制基类名2,……访问控制基类名n{数据成员和成员函数说明};•类可以从一个基类或多个基类派生新类,若基类只有一个称单继承,而多个基类则称为多继承。•其中,“访问控制”是表示继承权限的关键字,称为访问描述符,它可以是;public公有继承、private私有继承和protected保护继承。若省略访问描述符,C++认为是私有继承。派生类不同的继承方式对基类成员有不同的访问权限。2.继承成员的访问控制规则:如果类B继承类A,那么在类B的成员函数中可以使用类A的哪些数据成员与成员函数?其他类又可以使用类B中继承下来的哪些成员?这都是由类A中声明成员的访问控制方式以及类B继承类A时声明的继承访问控制方式两者共同决定的。继承成员的访问控制规则就是指派生类从基类中继承过来的成员的访问控制:派生类按指定的访问控制(继承访问控制)从基类中继承具有不同访问控制的成员,这些成员在派生类中,其访问控制发生了什么变化。程序员可以使用访问控制规则来设计基类与派生类,以权衡类的安全性与灵活性。基类成员访问控制继承访问控制派生类中的访问控制publicpublicpublicprotectedprotectedprivate不可访问publicprotectedprotectedprotectedprotectedprivate不可访问publicprivateprivateprotectedprivateprivate不可访问注意:“不可访问”与private是有区别的,private成员可以由派生类自身访问,“不可访问”则连派生类自身也不可以访问,基类中不可访问的成员在派生类中也是不可访问的。3.不能被继承的成员不是所有的C++成员都可以通过继承来传递的。主要有下面这些:•用户重载的构造函数。•用户重定义的析构函数。•用户定义的新操作符。•用户定义的赋值符。•友元关系。派生类在实例化时自动调用基类的构造函数。尽管基类的构造函数被继承了,但它们只能在建立派生类时由编译器自动调用。基类的构造函数不能象其他成员函数那样显式地调用。同样,析构函数是对象退出作用域时自动调用的,也不允许显式地调用析构函数。3.构造函数、析构函数的调用顺序如果一个类没有声明任何构造函数和析构函数,C++编译器将为该类自动生成一个缺省的构造函数。缺省的构造函数不带任何形式参数,并且函数体为空。在一个类实例化时,需要调用其构造函数。如果这个类是派生类,就还必须调用其基类构造函数。在C++语言中,基类构造函数的调用顺序是固定的。也就是先建立基类,然后再建立派生类。表现在调用构造函数上,就是先调用基类的构造函数然后再调用派生类的构造函数。析构函数的调用顺序与构造函数的调用顺序正好相反。首先调用派生类的析构函数,然后再调用基类的析构函数。实例化派生类时,经常需要使用基类的构造函数,而这些构造函数常常需要参数。可以用特定的表示法向基类构造函数传递参数。如果基类的构造函数带有参数,那么在派生类构造函数实现时必须使用初始化列表将参数传递给基类。带初始化参数列表的派生类构造函数一般形式为:派生类名(参数表):基类名(调用基类构造函数参数表){派生类构造函数体}如:实验二例子CIRCLE(inta,intb,intr){set_point(a,b);radius=r;perimeter=2*3.14159*radius;area=3.14159*radius*radius;}……CYLINDER::CYLINDER(inta,intb,intr,inth):CIRCLE(a,b,r){height=h;}4.继承成员的调整在一个派生类中,可以引入新的数据成员和成员函数。但有时从基类继承下来的成员不能满足要求时,就必须对这些成员进行调整。允许程序员根据需要对继承成员进行调整是继承机制的一个重要方面。不同面向对象程序设计语言提供的调整能力也不同。C++语言提供的调整机制包括:•恢复访问控制方式•继承成员的重定义•继承成员的重命名•屏蔽继承成员4.1恢复访问控制方式在一些应用中,希望从基类中继承的大多数公有成员都变成私有的或受保护的,进其中一部分仍保持为公有成员。使用私有或受保护派生类可以屏蔽基类继承下来的公有成员,但这样又导致所有公有成员均被屏蔽。可以使用访问声明将一些被屏蔽的公有成员恢复到原来的访问控制状态。访问声明采用作用域运算符::,它的一般形式为:基类名::成员名;在派生类的类接口中,将这些访问声明放在合适的访问控制域中,从而改变在派生类中该成员的访问控制方式。访问声明除了改变访问控制方式之外不做任何事情,因而在访问声明中不允许出现类型。如果成员是一个函数,那么表示函数的左、右括号也不能出现。注意:访问声明只能将继承的成员恢复到原来的访问控制方式,而不能改变其原来在基类中的访问控制方式。//演示在派生类中如何调整从基类继承下来的成员的访问控制方式#includeiostream.hClassBASE{public:voidset_i(intx){i=x;}intget_i(){returnI;}protected:intI;};ClassDERIVED:privateBASE//基类中的所有成员在派生类中都为私有成员{public:BASE::set_i;//访问声明,恢复原有基类中的访问控制方式voidset_j(intx){j=x;}intget_jj(){returni+j;}protected:intj;};Intmain(){DERIVEDobj;//声明一个派生类对象obj.set_i(5);//调用基类的成员函数obj.set_j(7);coutobj.get_ij()“\n”;return0;}4.2继承成员的重定义继承成员的重定义是指在语义上重新修改继承成员函数的实现。如果在派生类中定义了一个函数原型与继承成员函数一模一样的成员函数,则该函数实现的函数体是对继承成员函数的重定义。如果对一个派生类的对象实例使用这个函数名进行函数调用,那么使用的将是派生类中定义的成员函数,而不是原有的继承成员函数。这是因为编译程序搜寻调用对象的一个成员函数时,首先是在派生类中查找是否有该成员函数的定义,有则调用派生类的成员函数,否则才继续查找基类对该成员函数的定义,直到查找完所有的祖先类。示例见实验三。4.3继承成员的重命名继承成员的重命名是指在派生类中不改变继承成员的语义,仅为该成员起一个新的名字。重命名主要用于解决两个问题:一是解决名字冲突问题,产生命名冲突的情况是在多重继承时,一个类的多个基类可能包含相同的名字;二是允许派生类根据新的应用环境选择更适合的命名使得继承成员的名字更容易理解。C++并没有提供直接的重命名机制。常用的做法是在派生类中增加一个以新名字命名的成员函数。该成员函数的实现仅仅是调用旧名字的函数,然后根据需要决定是否要屏蔽旧名字的继承成员函数。如:RECTANGLE::sizeLeight(){CIRCLE::showPerimeter();}当然也可以在派生类中增加一个以新名字命名的成员函数,这个新成员函数的函数体和使用旧名字的函数完全相同。4.4屏蔽继承成员在C++语言中无法在派生类中删除从基类继承下来的成员以节省存储空间,即使派生类无法访问一些继承下来的成员,C++编译器也会为这些成员分配内存空间。而通过屏蔽继承成员,就可以实现在派生类中不能再访问从基类继承过来的某些成员。如果要在一个公有派生类中屏蔽从基类继承下来的公有成员函数,通常在派生类的protected或private域中重新定义一个完全相同的函数原型,然后函数体为空,这样使得原有的继承成员函数被派生类新定义的成员函数所支配,而新定义的成员函数则为不可访问的。5.多重继承如果在定义一个派生类时,该派生类继承了多个基类的特征,那么这种继承方式就叫多重继承(multipleinheritance)。多重继承也是一种is-a关系。一个多重继承很好的范例就是在C++定义的库文件中广泛使用了多重继承。如我们在程序中经常使用了输入/输出流库iostream.h所提供的服务。这个库的许多类也都是沿用继承机制组织起来的。输入/输出流同时具有输入流和输出流的特征,因而iostream.h同时继承了istream.h和ostream.h两个类。istream.hiostream.hostream.h5.1多重继承的名字冲突问题如果面向对象程序设计语言支持多重继承,那么就的面对多重继承中出现的名字冲突问题。所谓名字冲突(nameclash)是指两个基类都具有相同名字的成员时,在派生类中这个名字会产生二义性(ambiguity),即编译程序无法确定派生类的对象使用该名字时应该调用哪个基类中的版本。通常解决这一问题所持的基本观点是:首先这是一个语法问题而不是一个语义问题,没有必要去修改成员的语义;其次,没有任何理由为此修改基类的成员。碰到多重继承产生的名字冲突时,不同的面向对象程序设计语言有不同的处理方法。只要在程序中不使用这个类的对象实例中会引起二义性的成员,C++语言并不禁止名字冲突问题的产生,而是在问题出现后解决它。解决名字冲突问题通常有两种做法:•一是使用作用域运算符“::”;•二是重定义名字冲突的成员后者是更可取的做法。#includeiostream.hclassBASE1{public:voidshow(){couti“\n”;}protected:inti;};classBASE2{public:voidshow(){coutj“\n”;}protected:intj;};classDERIVED:publicBASE1,publicBASE2{public:viodset(intx,inty){i=x;j=y;}}//采用作用域运算符的解决方法intmain(){DERIVEDobj;obj.set(5,7);//obj.show();二义性错误,编译程序无法决定调用哪个基类的版本obj.BASE1::show()//显式地调用从BASE1继承下来的成员show()obj.BASE2::show()//显式地调用从BASE2继承下来的成员show()return0;}//用重定义有名字冲突的成员的解决方法classDERIVED:publicBASE1,publicBASE2{public:viodset(int
本文标题:第五章 类的继承与派生
链接地址:https://www.777doc.com/doc-3358113 .html