继承与派生.docx
- 文档编号:24177391
- 上传时间:2023-05-25
- 格式:DOCX
- 页数:13
- 大小:17.95KB
继承与派生.docx
《继承与派生.docx》由会员分享,可在线阅读,更多相关《继承与派生.docx(13页珍藏版)》请在冰豆网上搜索。
继承与派生
一、类的继承与派生
1、三种继承方式:
公有、私有、保护
不同继承方式的影响主要体现在:
●派生类成员对基类成员的访问控制
●派生类对象对基类成员的访问控制
实际应用中,公有派生用的最为广泛,私有派生相对较少,而保护派生则极少使用
2、派生类对基类成员的继承与否是固定的,但程序员可以在派生类中对继承来的成员进行调整,包括修改成员的访问属性和覆盖原有的成员。
(1)修改成员的访问属性:
通过不同的继承方式可以改变成员的访问方式
(2)覆盖原有的成员:
在派生类中定义与基类同名的数据成员或具有相同函数名和参数的成员函数。
(3)在派生类中可以增加新的成员,增加的这部分内容体现了派生类对基类功能的扩展。
【例题:
】某IT公司有如下类型的技术人员:
程序员、项目经理,他们能完成不同的工作。
对每类技术人员的描述如下:
项目经理:
员工编号、姓名、所在部门、基本工资、项目提成
程序员:
员工编号、姓名、所在部门、基本工资、奖金
所有的员工都能输出自己的基本信息,包括员工编号、姓名和所在部门。
所有的员工都能计算自己的工资:
其中程序员工资等于基本工资加上奖金;项目经理的工资等于基本工资加上项目提成。
要求:
用类的继承与派生机制设计描述上述各类技术人员的类。
源代码如下:
#include
#include
usingnamespacestd;
//定义员工类
classEmployee{
protected:
//不能定义成private:
intid;//员工编号
stringname;//姓名
stringdepartment;//所在部门
doublebase_pay;//基本工资
public:
Employee(inta,strings1,strings2,doublep){id=a;name=s1;department=s2;base_pay=p;};//构造函数
EMPLOYEE(){};
voidSetID(inti){id=i;}
voidSetName(stringn){name=n;}
voidSetDepartment(stringd){department=d;}
voidSetBase_pay(doublebp){base_pay=bp;}
voidPrintInfo();//显示员工基本信息
doublesalary();//计算员工工资
};
voidEmployee:
:
PrintInfo()
{
cout<<"员工编号:
"< cout<<"姓名: "< cout<<"员工所在部门: "< } doubleEmployee: : salary(){returnbase_pay;} //定义程序员类 classProgrammer: publicEmployee { private: doublebonus; stringbm;//添加新的成员“奖金” public: programmer(){}; voidSetBonus(doubles){bonus=s;} doublesalary(){returnbase_pay+bonus;} //覆盖从基类继承的已经不再适用的成员函数 /*不能在基类中将base_pay定义成private,否则在这里不能访问,所以要定义成protected*/ }; Programmer: : programmer(inta,stringn,stringbm,doublep,doubleb): A(),employee(a,n,bm,p){bonus=p;} //定义项目经理类 classProjectManager: publicEmployee,publicA { private: doublecommission;//项目提成 public: voidSetCommission(doublec){commission=c;} doublesalary(){returnbase_pay+commission;} }; voidmain() { //Employeee; //Programmere; ProjectManagere; e.SetID(110); e.SetName("john"); e.SetDepartment("开发部"); e.SetBase_pay(2000); e.SetCommission(1000); e.PrintInfo(); cout<<"工资是: "< } 二、继承关系中成员的访问控制 1、公有派生的访问控制 基类中所有成员在公有派生类中保持各个成员的原有访问权限。 其中,基类的私有成员(private)在派生类中仍为私有成员。 但派生类不能直接使用基类中的私有成员,只能通过该基类公有的或保护的成员函数间接使用基类中的成员。 2、私有派生的访问控制 基类中公有成员和保护成员在派生类中均变为的,在派生类中可直接使用这些成员,但基类的私有成员与公有派生情况相同,在派生类中不能直接使用。 3、保护派生的访问控制 基类的公有成员和保护成员在派生类中都成为保护成员,基类的私有成员在派生类中仍然是私有成员,且不能被派生类直接访问。 【例如: 】用以下代码证明在公有继承中: (1)基类中的公有成员在派生类中仍然是公有成员 (2)基类中的私有成员在派生类中仍然是私有成员,派生类不能直接使用基类中的私有成员,只能通过基类公有的或保护的成员函数间接使用基类中的私有成员 (3)基类中的保护成员在派生类中仍然是保护成员 #include #include usingnamespacestd; classbase { private: voidf1(){cout<<"thisisaprivate"< protected: voidf2(){cout<<"thisisaprotected"< public: voidf3(){cout<<"thisisapublic"< voidtest() { cout<<"在base内调用公有的f3"; f3(); } }; //定义一个公有派生类 classsub: protectedbase{ public: voidsubtest() { f1();//编译错误 f2();//正确 cout<<"在sub内调用test: "< test(); f3(); } }; voidmain() { subb; b.f1();//编译错误,证明基类中的私有成员在派生类中没有变成公有成员 b.f2();//编译错误,证明基类中的保护成员在派生类中没有变成公有成员 b.f3(); } 三、类层次结构中的构造函数和析构函数 构造函数和析构函数不能被继承,因此每个类需要定义自己的构造函数和析构函数。 1、派生类的构造函数 在派生类对象的成员中,从基类继承下来的成员被封装成基类子对象,派生类的构造函数要对派生类对象和基类子对象的创建负责。 因此,派生类构造函数需要隐含的或者显式地调用基类构造函数对基类子对象进行初始化。 派生类的构造函数通常采用初始化成员列表的方式调用基类的构造函数。 格式为: 派生类类名: : 派生类类名(参数): 基类1(参数),……,基类n(参数) {//派生类新增成员的初始化} (1)如果基类的构造函数是默认构造函数或是不带参数的构造函数,则在派生类中的构造函数中可以省略对基类的构造函数的调用,系统会自动先执行基类没有参数的构造函数; (2)如果基类没有不带参数的构造函数,则派生类中必须定义带参数的构造函数并显式调用基类的构造函数; (3)派生类自己的初始化可在参数列表中完成,也可以在构造函数体内完成; (4)基类构造函数的调用顺序只与派生类继承基类的顺序有关,而与初始化成员列表中构造函数的排列顺序无关。 (5)单继承时,派生类构造函数调用的一般顺序如下: a、调用基类的基类的构造函数 b、调用基类的构造函数 c、执行派生类自己的构造函数 2、派生类的析构函数 在派生类中可以根据需要定义自己的析构函数,用来对派生类中心增加的成员进行清理。 派生类中的基类子对象的清理工作仍然由基类的析构函数负责,但需要通过派生类的析构函数去调用基类的析构函数。 由于析构函数无参数、无类型,所以定义派生类的析构函数相对简单。 在执行派生类的析构函数时,系统会自动调用基类的析构函数,对基类子对象进行清理。 派生类对象执行析构函数时,各析构函数的调用顺序与构造函数的调用顺序正好相反,其调用顺序如下: a、调用派生类的析构函数 b、调用基类的析构函数 c、调用基类的基类的析构函数 四、基类与派生类的关系 1、基类与派生类定义的成员的优先关系 #include #include usingnamespacestd; classA { protected: intx; public: voidshow(){cout<<"x="< }; classB: publicA { protected: intx; public: int&getx(){returnx;} int&getAx(){returnA: : x;} }; voidmain() { Bb; b.getx()=10; b.getAx()=100; cout<<"B: : x="< cout<<"A: : X="< } 注意: 派生类定义的成员优先于基类中的同名成员 2、类型兼容 对于公有派生类来说,可以将派生类的对象赋值给基类对象,反过来则不可以。 类型兼容是指在公有派生的前提下,一个派生类对象可以作为基类的对象来使用,类型兼容有时也被称作赋值兼容或类型适应。 #include #include usingnamespacestd; classpoint { protected: intx,y; public: point(inta,intb): x(a),y(b){} voidshow(){cout<<"(x="< }; classcircle: publicpoint { protected: doubleradius; public: circle(doubler,intx,inty): point(x,y){radius=r;} //voidshow(){cout< voidshowcircle() { cout<<"centerofcircleis"< show(); cout<<"ris: "< } }; voidmain() { doubler=1; intx1=10,y1=10,x2=2,y2=2; pointp1(x1,y1),*pp; circlec1(r,x2,y2),*cp; pp=&p1; cp=&c1; cp->showcircle(); pp->show(); pp=cp;//将派生类对象指针赋值给基类对象 //p1=c1; pp->show(); pp=&c1;//将派生类对象地址赋值给基类对象 pp->show(); } 思考: 如果main函数中出现下面的语句,程序会怎样? 为什么? cp=pp; cp->showcircle(); cp=&p1; cp->show(); 五、多继承 1、多继承的构造函数和析构函数 构造函数的调用顺序如下: a、调用各基类的构造函数。 调用顺序按照派生类定义时声明基类的顺序进行,依次从左至右调用各个基类的构造函数 b、调用内嵌成员对象的构造函数 c、调用派生类的构造函数 析构函数顺序刚好相反。 2、多继承的歧义性与解决办法 情况一: 例如: classA { public: voidprint(); …… }; classB { public: voidprint(); voidfun(); …… }; classC: publicA,publicB { Xa; public: voidfun(); //voidprint(); …… }; 定义: Cc1;c1.fun();c1.print();发生歧义 解决办法: c1.fun(); c1.B: : fun(); c1.A: : print(); c1.B: : print(); 歧义情况二: classA { public: inta; }; classB: publicA { public: intb; }; classC: publicA { public: intc; }; classD: publicB,publicC { …… //Public: inta; }; 如下发生歧义: Dd;d.ad.A: : a; 解决办法: d.B: ;a;d.C: : A
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 继承 派生