第4章继承性与派生类本章的学习目的客观事物姿态万千、层出不穷,新事物在继承旧事物基本属性的基础上增添新的内容,使客观世界更加丰富多彩。作为模拟人们理解和处理客观世界思维方式的面向对象技术,它也提供了继承的机制,使继承性成为了C++面向对象技术的最重要的基本特征。即C++面向对象技术将事物抽象成类,允许利用已有类定义新的类,并将类组织成合理的层次结构,以精确地描述客观事物之间的联系。C++的这种继承机制为编程人员提供了重复利用程序资源的一种途径,编程人员可以扩充和完善旧的程序以适用新的需求,可用继承机制实现代码重用和代码扩充,大大提高程序的开发效率。因此,我们在前面学习了类的定义和构造设计的基础上,应该进一步研究类之间的关系,特别地应该学习类之间的继承性和派生性,以全面掌握C++面向对象程序设计方法。本章的学习内容•继承的概念•派生类的定义•派生类的构造函数和析构函数•继承属性的访问权限控制•多继承的概念类的继承:就是根据一个类创建一个新类的过程。类的派生:从已有类产生新类的过程就是类的派生。通常将用来派生新类的类称为基类(或父类),而将派生出来的新类称为派生类(或子类)。新类自动具有已有类的所有成员,并可根据需要添加更多的成员。操作:查询属性get();修改属性modify();打印属性print();属性:学号;姓名;专业;学生类操作:查询属性get();修改属性modify();打印属性print();列出成绩表grades();属性:学号;姓名;专业;班级;本科生类操作:查询属性get();修改属性modify();打印属性print();显示研究成果achv();属性:学号;姓名;专业;研究方向;研究生类图1-1类层次结构4.1继承、派生和类的层次关系4.2派生类派生类是在继承基类的属性和操作的基础上增添新的属性和操作而产生的新类。派生类是基类的特殊子类,基类是抽取派生类的主要属性和操作而得到的抽象描述。派生类继承了基类的所有特性,但不等同于基类,否则就没有派生的必要了。继承关系体现了特殊与一般的关系。操作:查询属性get();修改属性modify();打印属性print();属性:学号;姓名;专业;学生类操作:查询属性get();修改属性modify();打印属性print();列出成绩表grades();属性:学号;姓名;专业;班级;本科生类操作:查询属性get();修改属性modify();打印属性print();显示研究成果achv();属性:学号;姓名;专业;研究方向;研究生类图1-1类层次结构继承的一个作用就是允许派生类在继承父类共性的基础上,增加新的属性和操作来实现特殊功能;另一个作用是代码重用,从基类派生子类,子类无需修改基类的代码,就可以直接拥有基类的成员,然后增加少量代码就可以实现特殊功能,这就实现了代码的重用。操作:查询属性get();修改属性modify();打印属性print();属性:学号;姓名;专业;学生类操作:查询属性get();修改属性modify();打印属性print();列出成绩表grades();属性:学号;姓名;专业;班级;本科生类操作:查询属性get();修改属性modify();打印属性print();显示研究成果achv();属性:学号;姓名;专业;研究方向;研究生类图1-1类层次结构class派生类名:继承方式基类名{派生类新定义成员};其中,继承方式有三种:公有继承、私有继承和保护继承,分别用关键字public、private和protected表示。缺省情况下为私有继承。classperson//基类{protected:charname[11];charsex;intage;public:char*GetName();intGetSex();intGetage();};classStudent:publicperson{private:charid[9];floatscore;public:floatGetScore();};4.2.1派生类的定义【例4.1】派生类的定义classA{//定义一个基类A,A也称为超类、父类inti;public:voidset_i(intn){i=n;}intget_i(){returni;}};classB:publicA{//下划线处说明类B将继承类A的公有成员intj;public:voidset_j(intn){j=n;}intMultiply(){returnj*get_i();}//可调用基类的get_i()};//B不能访问A类的私有成员i。Main(){Bob;ob.set_i(10);//初始化ii,B可通过A类的set_i()访问iob.set_j(4);//初始化B中的jcoutob.Multiply();//运行结果为40return0;}派生类的生成包含三个步骤:(1)吸收基类成员吸收基类的部分成员,不吸收构造函数和析构函数。它是一个重用过程。(2)改造基类成员一是通过派生类定义时的继承方式来控制;二是通过定义同名成员屏蔽基类成员。它是一个扩充过程。4.2.2派生类的生成过程classperson//基类{protected:charname[11];charsex;intage;public:voidShow();};classStudent:publicperson{private:floatscore;public:voidShow(){person::Show();coutscoreendl;};};(3)添加派生类新成员仅仅继承基类的成员是不够的,需要在派生类中添加新成员,以保证派生类在功能上有所发展。同基类的构造函数和析构函数是不能被继承的,需要加入新的构造函数和析构函数完成一些特别的初始化和扫尾清理工作。classperson//基类{protected:charname[11];charsex;intage;public:voidShow();};classStudent:publicperson{private:floatscore;public:voidShow(){person::Show();coutscoreendl;};};4.3访问权限控制从派生类的定义格式可知,有三种继承方式:公有、私有和保护。因此,派生类对基类成员的访问权限控制也从三个方面考虑:(1)公有继承的访问权限控制;(2)私有继承的访问权限控制;(3)保护继承的访问权限控制;4.3.1公有继承的访问权限控制当类的继承方式为公有继承时:(1)在派生类中,基类的公有成员和保护成员被继承后仍然作为派生类的公有成员和保护成员,派生类的成员可以直接访问它们;(2)基类的私有成员无法继承为派生类的私有成员或其他成员,因此派生类的成员无法直接访问基类的私有成员。(3)在类外,派生类的对象只可以访问继承下来的基类公有成员。【例4.1】公有继承的访问权限控制classA{//定义一个基类A,A也称为超类、父类inti;public:voidset_i(intn){i=n;}intget_i(){returni;}};classB:publicA{//下划线处说明类B将继承类A的公有成员intj;public:voidset_j(intn){j=n;}intMultiply(){returnj*get_i();}//可调用基类的get_i()};//B不能访问A类的私有成员i。Main(){Bob;ob.set_i(10);//初始化i,B可通过A类的set_i()访问iob.set_j(4);//初始化B中的jcoutob.Multiply();//运行结果为40return0;}当类的继承方式为私有继承时:(1)在派生类中,基类的公有成员和保护成员被继承以后将作为派生类的私有成员,派生类的成员函数可以直接访问它们;(2)基类的私有成员没有被继承过来,因此派生类的成员无法访问基类的私有成员;(3)在类外,无法访问派生类对象中从基类继承的所有成员。(4)私有继承之后,基类的成员再也无法在以后的派生类中发挥作用,出于这种原因,一般不使用私有继承方式。4.3.2私有继承的访问权限控制例4-2私有继承举例#includeiostream.h#includestring.hclassPerson{private:charname[11];charsex;protected:intage;public:Person(constchar*Name,intAge,charSex);char*GetName(){returnname;}intGetage();intGetSex;};classStudent:privatePerson{private:charid[9];floatscore;public:Student(char*pName,intAge,charSex,char*pId,floatScore)……voidDisplay();};voidStudent::Display(){cout“name:”GetName()‘\t’//访问变为私有的基类成员函数cout“id:”id‘\t’;//直接访问本类私有成员cout“age:”age‘\t’;//访问基类的保护成员(变为私有的)cout“score:”scoreendl;}voidmain(){Students2(“wangmin”,20,’m’,”03410102”,80);s2.Display();}4.3.3保护继承的访问权限控制当类的继承方式为保护继承时:(1)在派生类中,基类的公有成员和保护成员被继承以后将作为派生类的保护成员,派生类的成员可以直接访问它们;(2)基类的私有成员没有被继承,因此派生类的成员无法访问基类的私有成员。(3)在类外,无法访问派生类对象中从基类继承的所有成员。(4)与私有继承不同的是,保护继承还没有完全终止基类的功能,即保护继承可以传递部分基类成员给派生类的派生类,而私有继承不可以。如果合理地利用保护继承,就可以在类的复杂层次关系中为共享访问与成员隐蔽之间找到一个平衡点,既实现部分成员隐蔽,又能方便部分成员的继承,实现代码的高效重用和扩充。表4-1表表表表表表表表表表表表表表表表表表表表表表表表表表表表表表表表表表表表表表表表表表表表表表表表表表表表表表表表表表表表表表表表表表表表表表表表表表表表不论哪种继承方式,派生类新定义成员均不能直接访问基类的私有成员,只能通过基类的公有成员函数或保护成员函数访问基类的私有数据成员,而基类的私有成员函数根本就不会继承,更谈不上使用。所以,除非仅限于本类使用,否则,一般不将成员函数定义为私有成员。在定义派生类的构造函数时除了对自己的数据成员进行初始化外,还必须调用基类的构造函数初始化基类的数据成员。如果派生类中还有对象成员时,还应调用对象成员类的构造函数初始化对象成员。派生类构造函数的一般格式如下:派生类名(总参数表):基类名1(参数表),…,子对象名1(参数表),…,子对象名n(参数表){派生类数据成员的初始化};5.4.1派生类的构造函数例4-3派生类构造函数定义举例#includeiostream.h#includestring.hclassPerson{private:charname[11];charsex;protected:intage;public:Person(constchar*Name,intAge,charSex);char*GetName(){return(name);}intGetage();intGetSex;};classStudent:publicPerson{private:charid[9];floatscore;public:Student(char*pName,intAge,cha