c-应用心理学第8章继承与多态_第1页
c-应用心理学第8章继承与多态_第2页
c-应用心理学第8章继承与多态_第3页
c-应用心理学第8章继承与多态_第4页
c-应用心理学第8章继承与多态_第5页
已阅读5页,还剩57页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

《计算基础——C++语言实现》继承与多态是面向对象程序设计的两个重要特性。通过继承,可以基于已有类定义新类,实现软件复用。新类继承了已有类的属性和方法,还可以根据需要添加新的属性和方法。“继承”这种复用方式缩短了软件开发时间,使得开发人员可以复用已经测试和调试好的高质量的类,减少了系统投入使用后可能出现的问题。通过多态,可以使得在执行同一条语句时,能够根据运行情况执行不同的操作。“多态”使得设计和实现易于扩展的系统成为可能。第8章继承与多态28.1.1概述继承是类的一个重要特性,它允许程序员基于已有的类创建自己的新类,而不必从头编写代码。在C++中,如果一个类C1通过继承已有类C而创建,则将C1称作派生类(也称做子类),将C称做基类(也称做父类)。派生类会继承基类中定义的所有属性和方法,另外也能够在派生类中定义派生类所特有的属性和方法。为了更好的理解继承的概念,我们举例说明,如图8-1所示。8.1继承38.1继承4以Person类作为基类,创建了Student类、Teacher类等派生类。Student类和Teacher类从Person类继承了Name(姓名)、Sex(性别)等属性及SetName()、GetName()等方法,另外还添加了新的属性和方法。例如,在Student类中新添加了SNO(学号)、Major(专业)等属性及SetSNO()、GetSNO()等方法;在Teacher类中新添加了TNO(教师号)、Depart(系)等属性及SetTNO()、GetTNO()等方法。再同时以Student类和Teacher类作为基类,派生出了TA类。从图8-1可以看出,既可以以一个已有类作为基类创建新类,也可以以多个已有类作为基类创建新类。如果一个派生类是基于一个基类创建的,则该继承关系称为单继承;如果一个派生类是基于多个基类创建的,则该继承关系称为多重继承。8.1继承5提示: 一个类是基类还是派生类是针对具体的继承关系而言的。一个类可能在一个继承关系中是基类,而在另一个继承关系中是派生类。例如,在图8-1中,Student类和Teacher类都是基于Person类创建的,因此,在这两个继承关系中,Student类和Teacher类是派生类,Person类是基类。而在另一个继承关系中,TA类是基于Student类和Teacher类创建的,因此,TA类是派生类,而Student类和Teacher类都是基类。 一般来说,派生类所表示的事物是基类所表示事物的子集(即派生类所表示的事物比基类更具体)。因此,在一个继承关系中,“派生类事物是基类事物”这句话肯定成立,但反过来却不行。例如,对于Teacher类和Person类的继承关系来说,Teacher类是派生类,Person类是基类,显然“Teacher是Person”这句话是正确的,而反过来说“Person是Teacher”则不行。我们在定义继承关系时可以参照上述方法来检验所定义的继承关系是否合理。8.1继承68.1.2派生类定义(1)定义派生类的语法在一个继承关系中,定义派生类的语法为:

class派生类名:继承方式基类名

{

派生类成员声明; };其中,继承方式包括public(公有继承)、protected(保护继承)和private(私有继承)三种,其含义将在后面章节中介绍。这里我们先使用public作为继承方式。例如,对于图8-1所示的Person类和Student类的单继承关系,我们可以先定义Person类,再以Person类作为基类派生出Student类。8.1继承7【例8-1】定义Person类及其派生类Student类。//derivedClassDef.cpp#include<iostream>usingnamespacestd;classPerson{public: voidSetName(char*name){strcpy(m_name,name);} char*GetName(){returnm_name;} voidSetSex(boolsex){m_sex=sex;} boolGetSex(){returnm_sex;}private: charm_name[20]; //姓名

boolm_sex; //性别(true:男,false:女)};8.1继承8classStudent:publicPerson{public: voidSetSNO(char*sno){strcpy(m_sno,sno);} char*GetSNO(){returnm_sno;} voidSetMajor(char*major){strcpy(m_major,major);} char*GetMajor(){returnm_major;} voidDisplayInfo()

{ cout<<"学生信息:"<<endl <<"学号:"<<m_sno<<endl <<"姓名:"<<GetName()<<endl <<"性别:"; if(GetSex()==true)cout<<"男"<<endl; elsecout<<"女"<<endl; cout<<"专业:"<<m_major<<endl; }private: charm_sno[8]; //学号 charm_major[20]; //专业};8.1继承9intmain(){ Studentstudent; student.SetSNO("1210101"); student.SetName("张三"); student.SetSex(true); student.SetMajor("计算机应用"); student.DisplayInfo(); return0;}上面的程序中,Student类和Teacher类继承了Person类的所有数据成员和方法成员。程序运行后,会在屏幕上输出: 学生信息: 学号:1210101

姓名:张三 性别:男

专业:计算机应用8.1继承10基类中的私有成员在派生类的成员函数中无法直接访问。例如,如果将DisplayInfo()函数中的GetName()和GetSex()函数调用分别改为m_name和m_sex,则编译程序时会报错。如果希望在派生类中直接访问基类的成员,则可以将基类的成员声明为public或protected。表8-1给出了public、private和protected三种类成员访问控制方式的含义。8.1继承访问控制方式含义public类的公有成员,在任何地方都可以直接访问private类的私有成员,只在该类的成员函数中可以直接访问,在其他地方均不能直接访问protected类的保护成员,在该类及其派生类的成员函数中可以直接访问,在其他地方不能直接访问11例如,只要将例8-1中的Person类定义改为: classPerson { public: voidSetName(char*name){strcpy(m_name,name);} char*GetName(){returnm_name;} voidSetSex(boolsex){m_sex=sex;} boolGetSex(){returnm_sex;} protected: charm_name[20]; //姓名 boolm_sex; //性别(true:男,false:女) };就可以在Student类的成员函数中直接访问Person的私有数据成员m_name和m_sex.8.1继承12提示: 用一个类作为基类来派生新类之前,必须要先定义,例如下面的程序就是错误的: classPerson; //声明,未定义 classStudent:publicPerson //错误:在继承前没有给出Person类的定义 { …… };8.1继承13(2)函数重定义对于基类中的成员函数,可以在派生类中对其重新定义、实现新的功能。例如,我们可以在例8-1的Person类中也定义一个DisplayInfo()函数:

voidDisplayInfo() { cout<<"个人信息:"<<endl <<"姓名:"<<m_name<<endl <<"性别:"; if(m_sex==true)cout<<"男"<<endl; elsecout<<"女"<<endl; }8.1继承14这样,Student类对从Person类继承的DisplayInfo()函数进行了重定义。当使用Person类对象调用时,就会调用Person类中定义的DisplayInfo()函数;而当使用Student类对象调用时,就会调用Student类中定义的DisplayInfo()函数。例如,在主函数中使用如下语句: Personperson; person.SetName("李四"); person.SetSex(false); person.DisplayInfo();可以在屏幕上输出: 个人信息: 姓名:李四 性别:女8.1继承15提示:区分函数重定义和函数重载:函数重载要求函数形参不同(或者形参个数不同,或者形参类型不同),在实际调用时根据传入的实参来决定调用哪个函数;函数重定义则要求派生类中的函数原型与基类中的函数原型完全一样,在实际调用时根据对象类型来决定调用基类中定义的函数还是派生类中重定义的函数。8.1继承168.1.3派生类的构造函数和析构函数派生类构造函数的作用主要是对派生类中新添加的数据成员做初始化工作;在创建派生类对象、执行派生类构造函数时,系统会自动调用基类的构造函数来对基类中定义的数据成员做初始化。同样,派生类析构函数的作用主要是清除派生类中新添加的数据成员、释放它们所占据的系统资源;在销毁派生类对象、执行派生类析构函数时,系统会自动调用基类的析构函数来释放基类中数据成员所占据的系统资源。8.1继承17(1)派生类构造函数的定义派生类中构造函数的定义有以下两种形式:■形式1

派生类名(形参列表):基类名(实参列表) { //派生类中数据成员的初始化

…… }通过“基类名(实参列表)”可以调用基类构造函数、对派生类从基类继承的数据成员进行初始化。可以将派生类构造函数形参列表中的形参作为实参传递给基类构造函数,也可以将常量、全局变量等作为实参传递给基类构造函数。8.1继承18例如,我们可以为Person类定义构造函数:

Person(char*name,boolsex) { …… }那么Student类的构造函数定义为:

Student(char*sno,char*name,boolsex,char*major):Person(name,sex) { …… }当以下面语句创建Student类对象student时,就会将"1210101"和true作为实参传递给基类Person的构造函数。

Studentstudent("1210101","张三",true,"计算机应用");8.1继承19■形式2

派生类名(形参列表) { //派生类中数据成员的初始化

…… }形式2会自动调用基类的无参构造函数、对派生类从基类继承的数据成员进行初始化。它等价于:

派生类名(形参列表):基类名() { //派生类中数据成员的初始化

…… }8.1继承20(2)派生类析构函数的定义派生类中析构函数的定义形式与基类完全相同,其语法格式为:

~派生类名() { //释放派生类数据成员所占的系统资源

…… }【例8-2】为基类Person和派生类Student定义构造函数和析构函数。8.1继承21//derivedClassDef.cpp#include<iostream>usingnamespacestd;classPerson{public: Person(char*name,boolsex) { strcpy(m_name,name); m_sex=sex; cout<<"Person类构造函数被调用!"<<endl; } ~Person(){cout<<"Person类析构函数被调用!"<<endl;}private: charm_name[20]; //姓名

boolm_sex; //性别(true:男,false:女)};8.1继承22classStudent:publicPerson{public: Student(char*sno,char*name,boolsex,char*major):Person(name,sex) { strcpy(m_sno,sno); strcpy(m_major,major); cout<<"Student类构造函数被调用!"<<endl; } ~Student(){cout<<"Student类析构函数被调用!"<<endl;}private: charm_sno[8]; //学号

charm_major[20]; //专业};8.1继承23intmain(){ Studentstudent("1210101","张三",true,"计算机应用"); return0;}当创建派生类对象时,先调用基类的构造函数,再调用派生类的构造函数;析构函数调用顺序总是与构造函数调用顺序相反。因此,上面的程序运行后,会在屏幕上输出:

Person类构造函数被调用!

Student类构造函数被调用!

Student类析构函数被调用!

Person类析构函数被调用!8.1继承248.1.4派生类的继承方式在定义派生类时,可以指定的继承方式包括public(公有继承)、protected(保护继承)和private(私有继承,缺省方式)3种。通过设置继承方式,可以使基类成员的访问控制方式在派生类中发生变化。表8-2是上述3种继承方式的总结。可见,派生类从基类继承过来的成员的访问控制方式由两点决定:该成员在基类中的访问控制方式;定义派生类所采用的继承方式。8.1继承访问控制方式继承方式publicprivateprotectedpublicpublic不可访问protectedprivateprivate不可访问privateprotectedprotected不可访问protected25以public(公有方式)继承时,基类的公有成员和保护成员的访问控制方式在派生类中保持不变,仍作为派生类的公有成员和保护成员,基类的私有成员在派生类中不能直接访问。以private(私有方式)继承时,基类的公有成员和保护成员在派生类中都作为私有成员,基类的私有成员在派生类中不能直接访问。以protected(保护方式)继承时,基类的公有成员和保护成员在派生类中都作为保护成员,基类的私有成员在派生类中无法直接访问。8.1继承268.1.5多重继承(1)多重继承方式下的派生类定义在图8-1中,通过对Student类和Teacher类进行多重继承得到TA类。在一个多重继承关系中,定义派生类的语法为:

class派生类名:继承方式基类名1,继承方式基类名2,…,继承方式基类名n {

派生类成员声明; };其中,不同的基类可以使用不同的继承方式。【例8-3】实现图8-1的程序代码。8.1继承27classStudent:publicPerson{public: Student(char*sno,char*name,boolsex,char*major):Person(name,sex) { strcpy(m_sno,sno); strcpy(m_major,major); cout<<"Student类构造函数被调用!"<<endl; } ~Student(){cout<<"Student类析构函数被调用!"<<endl;} voidSetSNO(char*sno){strcpy(m_sno,sno);} char*GetSNO(){returnm_sno;} voidSetMajor(char*major){strcpy(m_major,major);} char*GetMajor(){returnm_major;}private: charm_sno[8]; //学号

charm_major[20]; //专业};8.1继承28classTeacher:publicPerson{public: Teacher(char*tno,char*name,boolsex,char*depart):Person(name,sex) { strcpy(m_tno,tno); strcpy(m_depart,depart); cout<<"Teacher类构造函数被调用!"<<endl; } ~Teacher(){cout<<"Teacher类析构函数被调用!"<<endl;} voidSetTNO(char*tno){strcpy(m_tno,tno);} char*GetTNO(){returnm_tno;} voidSetDepart(char*depart){strcpy(m_depart,depart);} char*GetDepart(){returnm_depart;}private: charm_tno[6]; //教师号

charm_depart[20]; //系};8.1继承29classTA:publicStudent,publicTeacher{public: TA(char*sno,char*name,boolsex,char*major,char*tno,char*depart) :Teacher(tno,name,sex,depart),Student(sno,name,sex,major) { cout<<"TA类构造函数被调用!"<<endl; } ~TA(){cout<<"TA类析构函数被调用!"<<endl;}};intmain(){ TAta("1210102","王五",true,"计算机应用","09110","计算机科学与技术系"); return0;}8.1继承30在创建通过多重继承定义的派生类对象时,也会先调用基类的构造函数,再调用派生类的构造函数。各基类构造函数的调用顺序与多重继承时的继承顺序一致。在创建TA类时,先继承Student类、再继承Teacher类,因此,会先调用Student类的构造函数、再调用Teacher类的构造函数。Student类和Teacher类又是Person类的派生类,因此,在执行Student类构造函数前会先调用Person类构造函数、在执行Teacher类构造函数前也会先调用Person类构造函数。在创建TA类对象时,程序会在屏幕上输出:

Person类构造函数被调用!

Student类构造函数被调用!

Person类构造函数被调用!

Teacher类构造函数被调用!

TA类构造函数被调用!析构函数调用顺序总是与构造函数调用顺序相反。8.1继承31(2)多重继承中的二义性问题在例8-3中,Student类和Teacher类都继承了Person类的成员,TA类继承了Student类和Teacher类的成员,因此,TA中包含了两份Person类的成员(分别从Student类和Teacher类继承)。如果我们通过TA类的对象调用Person类的成员,则在编译程序时会报错。如:cout<<ta.GetName()<<endl; //输出姓名这是由于TA类中有两个分别从Student类和Teacher类继承过来的GetName()函数,直接使用ta对象调用GetName()函数时会有二义性问题,即编译程序不知道应该调用哪个GetName()函数。8.1继承32为了解决这个问题,我们可以在调用GetName()函数时通过作用域运算符指定要调用从哪个类继承过来的函数。例如:

cout<<ta.Student::GetName()<<endl;或

cout<<ta.Teacher::GetName()<<endl;但这样调用函数需要知道类的继承关系,不方便类的使用。下面介绍另一种使用虚拟继承解决多重继承中二义性问题的方法。8.1继承33(3)虚拟继承和虚基类在定义派生类时,可以通过虚拟继承方式将基类声明为虚基类。虚基类中的成员在类的继承关系中只会被继承一次,从而解决上述二义性问题。虚拟继承的语法为:

class派生类名:virtual继承方式虚基类名

{ …… }其中,关键字virtual和继承方式的顺序可以调换。例如,在例8-3中定义Student类和Teacher类时,如果采用虚拟继承方式:

classStudent:virtualpublicPerson { …… } classTeacher:virtualpublicPerson { …… }8.1继承34则Person类成为虚基类。此时,TA类从虚基类Person类中继承Person类的成员,从Student类和Teacher类中则只继承了Student类和Teacher类新定义的成员,从而保证TA类中只包含一份Person类的成员、解决了二义性问题。由于虚基类后继类层次中的类都是直接从虚基类继承其成员,而对这部分成员的初始化需要调用虚基类的构造函数来完成。因此,如果一个派生类同时从基类和虚基类继承了成员,那么在定义该类的构造函数时,除了要调用基类的构造函数、还要调用虚基类的构造函数;析构函数也是如此,当销毁该派生类的对象时,除了会直接调用基类的析构函数、还会直接调用虚基类的析构函数。8.1继承35【例8-4】采用虚拟继承方式改写例8-3,解决二义性问题。//VirtualInherit.cpp#include<iostream>usingnamespacestd;classPerson{public: Person(char*name,boolsex) { strcpy(m_name,name); m_sex=sex; cout<<"Person类构造函数被调用!"<<endl;} ~Person(){cout<<"Person类析构函数被调用!"<<endl;} voidSetName(char*name){strcpy(m_name,name);} char*GetName(){returnm_name;} voidSetSex(boolsex){m_sex=sex;} boolGetSex(){returnm_sex;}private: charm_name[20]; //姓名

boolm_sex; //性别(true:男,false:女)};8.1继承36classStudent:virtualpublicPerson{public: Student(char*sno,char*name,boolsex,char*major):Person(name,sex) { strcpy(m_sno,sno); strcpy(m_major,major); cout<<"Student类构造函数被调用!"<<endl; } ~Student(){cout<<"Student类析构函数被调用!"<<endl;} voidSetSNO(char*sno){strcpy(m_sno,sno);} char*GetSNO(){returnm_sno;} voidSetMajor(char*major){strcpy(m_major,major);} char*GetMajor(){returnm_major;}private: charm_sno[8]; //学号

charm_major[20]; //专业};8.1继承37classTeacher:virtualpublicPerson{public: Teacher(char*tno,char*name,boolsex,char*depart):Person(name,sex) { strcpy(m_tno,tno); strcpy(m_depart,depart); cout<<"Teacher类构造函数被调用!"<<endl; } ~Teacher(){cout<<"Teacher类析构函数被调用!"<<endl;} voidSetTNO(char*tno){strcpy(m_tno,tno);} char*GetTNO(){returnm_tno;} voidSetDepart(char*depart){strcpy(m_depart,depart);} char*GetDepart(){returnm_depart;}private: charm_tno[6]; //教师号

charm_depart[20]; //系};8.1继承38classTA:publicStudent,publicTeacher{public: TA(char*sno,char*name,boolsex,char*major,char*tno,char*depart) :Teacher(tno,name,sex,depart),Student(sno,name,sex,major), Person(name,sex) //需要调用虚基类的构造函数

{ cout<<"TA类构造函数被调用!"<<endl; } ~TA(){cout<<"TA类析构函数被调用!"<<endl;}};intmain(){ TAta("1210102","王五",true,"计算机应用","09110","计算机科学与技术系"); cout<<ta.GetName()<<endl; //输出姓名

return0;}8.1继承39当创建TA类对象ta时,会先调用虚基类的构造函数,再按继承顺序调用基类的构造函数,最后调用TA类的构造函数,因此,程序会在屏幕上输出:

Person类构造函数被调用!

Student类构造函数被调用!

Teacher类构造函数被调用!

TA类构造函数被调用!析构函数的调用顺序与构造函数相反,因此,在程序结束、销毁ta对象前会在屏幕上输出:

TA类析构函数被调用!

Teacher类析构函数被调用!

Student类析构函数被调用!

Person类析构函数被调用!TA类只是直接从虚基类Person类中继承了一份Person类的成员,因此在调用“ta.GetName()”时不会有二义性问题。提示:虚基类Person类的构造函数只会在TA类的构造函数中调用一次,而在执行Student类和Teacher类的构造函数时不会再重复调用虚基类Person类的构造函数。8.1继承408.2.1类型兼容和多态性的概念类型兼容是多态性的前提,指在基类对象可以出现的任何地方,都可以用公有派生类的对象来替代。类型兼容所指的是如下三种情况:(1)可以用派生类对象为基类对象赋值;(2)可以用派生类对象初始化基类引用;(3)可以用派生类对象地址为基类指针赋值。例如,对于图8-1所示的类的继承关系,以下程序能正常编译和运行:

Studentstudent; Personperson,*pPerson; person=student; //用派生类对象为基类对象赋值

pPerson=&student;//用派生类对象地址为基类指针赋值

Person&rPerson=student;//用派生类对象初始化基类引用8.2多态41提示:需要派生类对象的地方,不能以基类对象来替代。就像“猴子是动物”是正确的,但“动物是猴子”就错了。提示:用派生类对象替代基类对象进行赋值操作后,通过基类对象、基类对象引用和基类指针只能访问派生类从基类继承的成员。例如:

cout<<person.GetName()<<endl; //正确:使用基类对象能够访问派生类从基

类继承的的成员

cout<<person.GetMajor()<<endl; //错误:使用基类对象不能访问派生类中新定义的成员8.2多态42通过类型兼容,对于基类及其公有派生类的对象,可以使用相同的函数统一进行处理。比如,函数参数是基类类型,而实际调用该函数时既可以传入基类对象,也可以传入派生类对象。【例8-5】类型兼容示例。//CompatibleType.cpp#include<iostream>usingnamespacestd;classPerson{public: Person(char*name,boolsex) { strcpy(m_name,name); m_sex=sex; }8.2多态43

voidDisplayInfo() { cout<<"个人信息:"<<endl <<"姓名:"<<m_name<<endl <<"性别:"; if(m_sex==true)cout<<"男"<<endl; elsecout<<"女"<<endl; }protected: charm_name[20]; //姓名

boolm_sex; //性别(true:男,false:女)};classStudent:publicPerson{public: Student(char*sno,char*name,boolsex,char*major):Person(name,sex) { strcpy(m_sno,sno); strcpy(m_major,major); }8.2多态44voidDisplayInfo() { cout<<"学生信息:"<<endl <<"学号:"<<m_sno<<endl <<"姓名:"<<m_name<<endl <<"性别:"; if(m_sex==true)cout<<"男"<<endl; elsecout<<"女"<<endl; cout<<"专业:"<<m_major<<endl; }private: charm_sno[8]; //学号

charm_major[20]; //专业};voidPrint(Person&rp){ rp.DisplayInfo();}8.2多态45intmain(){ Studentstudent("1210101","张三",true,"计算机应用"); Personperson("李四",false); Print(student); //以Student类对象作为实参

Print(person); //以Person类对象作为实参

return0;}上面的程序运行后,会在屏幕上输出: 个人信息: 姓名:张三 性别:男 个人信息: 姓名:李四 性别:女8.2多态46在主函数中两次调用Print()函数,分别将基类对象person和派生类对象student作为实参传递给基类引用rp。但是,在使用基类引用rp调用DisplayInfo()函数时都是调用基类定义的函数。显然,我们并不希望看到上面的运行结果。我们希望当以派生类对象作为实参传递给基类引用rp时,使用rp调用DisplayInfo()函数能够调用派生类定义的函数。这种能够根据指针或引用所表示的对象的实际类型来调用该对象所属类的函数、而不是每次都调用基类中函数的特性,就是本节所要介绍的多态性。下面介绍如何来实现多态性。8.2多态478.2.2多态性的实现(1)动态绑定与虚函数前面例子中的函数调用,都是采用“先期绑定”的方式。所谓“绑定”就是建立函数调用和函数本体的关联。如果绑定发生于程序运行之前(由编译器和链接器完成),则称为“先期绑定”(也称为“静态绑定”)。要实现多态性,就要进行“后期绑定”(也称为“动态绑定”),即绑定发生于程序运行过程中。C++通过虚函数实现“动态绑定”技术。虚函数的声明方法是在基类的函数声明前或函数定义的函数头前(无函数声明时)加上virtual关键字。8.2多态48例如,对例8-5中Person类的DisplayInfo()函数,只要在其函数头前加上virtual关键字:

virtualvoidDisplayInfo() { …… }该函数即成为虚函数。虚函数具有继承性,只要基类中的函数被声明为虚函数,则在派生类中对虚函数进行重定义时,无论是否加了virtual关键字,这个函数都是虚函数。【例8-6】多态性示例。//Polymorphism.cpp#include<iostream>usingnamespacestd;8.2多态49classPerson{public: Person(char*name,boolsex) { strcpy(m_name,name); m_sex=sex; } virtualvoidDisplayInfo() { cout<<"个人信息:"<<endl<<"姓名:"<<m_name<<endl<<"性别:"; if(m_sex==true)cout<<"男"<<endl; elsecout<<"女"<<endl; }protected: charm_name[20]; //姓名

boolm_sex; //性别(true:男,false:女)};8.2多态50classStudent:publicPerson{public: Student(char*sno,char*name,boolsex,char*major):Person(name,sex) { strcpy(m_sno,sno); strcpy(m_major,major); } voidDisplayInfo() //等价于virtualvoidDisplayInfo() { cout<<"学生信息:"<<endl <<"学号:"<<m_sno<<endl <<"姓名:"<<m_name<<endl <<"性别:"; if(m_sex==true)cout<<"男"<<endl; elsecout<<"女"<<endl; cout<<"专业:"<<m_major<<endl; }private: charm_sno[8]; //学号

charm_major[20]; //专业};8.2多态51voidPrint(Person&rp){ rp.DisplayInfo();}intmain(){ Studentstudent("1210101","张三",true,"计算机应用"); Personperson("李四",false); Print(student); //以Student类对象作为实参

Print(person); //以Person类对象作为实参

return0;}8.2多态52上面的程序运行后,会在屏幕上输出: 学生信息: 学号:1210101

姓名:张三 性别:男 专业:计算机应用 个人信息: 姓名:李四 性别:女与例8-5相同,在主函数中两次调用Print()函数,分别将基类对象person和派生类对象student作为实参传递给基类引用rp。但在例8-6中,DisplayInfo()函数被声明为虚函数,因此在使用基类引用rp调用该函数时就可以根据rp所引用对象的不同调用不同类的成员函数,即实现了多态性。8.2多态53提示: 只有使用指针或引用调用虚函数时才能实现多态性。如果使用对象调用虚函数,则不具有多态性,必然是调用该对象所属类的成员函数。例如,将例8-6中的Print()函数改为:

voidPrint(Personp) { p.DisplayInfo(); }

则运行结果与例8-5完全一样,不具有多态性。8.2多态54(2)虚析构函数如果使用基类指针释放动态创建的派生类对象,则需要将析构函数声明为虚函数。【例8-7】虚析构函数示例。//VirtualDestructor.cpp#include<iostream>usingnamespacestd;classPerson{public: Person(char*name,boolsex) { m_name=newchar[strlen(name)+1]; strcpy(m_name,name); m_sex=sex; cout<<"Person类构造函数被调用!"<<endl; }8.2多态55virtual~Person() //将析构函数声明为虚函数

{ delete[]m_name; cout<<"Person类析构函数被调用!"<<endl; }protected: char*m_name; //姓名

boolm_sex; //性别(true:男,false:女)};classStudent:publicPerson{public: Student(char*sno,char*name,boolsex,char*major):Person(name,sex) { m_sno=newchar[strlen(sno)+1]; m_major=newchar[strlen(major)+1]; strcpy(m_sno,sno); strcpy(m_major,major); cout<<"Student类构造函数被调用!"<<endl

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论