第一节类的继承与派生
一、基本概念
继承是面向对象程序设计的一个特性,所谓继承就是在已有类的基础上创建新的类,新类可以从一个或多个已有类中继承成员函数或数据成员,而且新类还可以重新定义或加进新的数据和函数。其中,已有类称为基类或父类,新类称为派生类或子类。
在现实世界中许多事物都具有继承性。例如:“汽车”这个类中包括了许多类型,有运输汽车、专用汽车;运输汽车中又包括客车、货车…….;专用汽车中又包括巡逻车、消防车、救护车……..;所有这些类型的车都具有汽车的共同特性即都有发动机、车身、轮胎等共性,还都是自行驱动的。
而客车和货车又有所不同,客车用来载客,货车用来拉货,它们有自己不同于其它车的特性,这就是继承。我们把汽车称为基类,把运输汽车、客车、货车称为派生类。通过继承,派生类不仅拥有了基类的属性和行为,而且具有不同于它类的自己的特点。
二、派生类的定义与构成
当你去水果店买水果的时候,你经常会发现很多同类水果又有了新的品种。比如,“李子”既小又涩,嫁接在梨树上后长出来的李子却硕大、香甜、清脆,比没有嫁接的李子好吃,但它还是李子家族中的一员;所以,可以认为“嫁接李子”是从“李子”中派生出来的。当然,可以把“李子”嫁接在梨树上,也可以嫁接在苹果树上,结出来的都是“李子”,但是各自味道不同。
所谓派生就是从已有类中产生一个新的子类。
基类就是已存在的可以派生新类的类,如下图中的A、D、E都是基类。
派生类就是由基类派生出来的新类,如下图中的B、C、F都是派生类。
1. 单继承派生类的定义格式
class <派生类名> : <继承方式> <基类名>
{
派生类成员的定义;
};
其中,“派生类名”是新定义的一个类的名字,它是从“基类名”中派生的,并且按指定的“继承方式”派生。
例如:
class peach 多继承派生类的定义格式
class <派生类名> :<继承方式1> <基类名>,<继承方式2> <基类名>, …….
{
派生类成员的定义;
};
多继承与单继承在定义格式的区别仅在于它们的基类个数不同,单继承的基类只有一个,而多继承的基类有多个,相邻两个基类之间用逗号间隔,每个基类名前都应有一个该基类的继承方式的说明符。
例如:
class peach //定义基类:peach
{
datatype color , shape ;
};
class apple //定义基类:peach
{
datatype color, shape , weight ;
};
class sweetpeach : public peach, public apple // 多继承
{
datatype color, shape, taste;
};
类sweetpeach 同时继承自类peach 和类apple,这就是多继承。
3.继承的作用
有的读者可能会问为什么要有继承,继承有什么好处,继承的作用是什么
为了解决这些疑问,我们一起来分析以下例子,从中领悟继承的奥妙。
【例10-1-1】分析以下代码。声明一个person 类,它包含的成员有name(姓名)、age (年龄)、gender(性别),和output函数,具体定义如下:
class person
{
public :
char name[8] ;
int age;
char gender ;
public :
void output() { }
};
现在要声明一个student类,它包含的成员有name(姓名),age(年龄),gender(性别),department(系),stuno(学号),address(住址),和output函数,具体定义如下:
class student
{
public :
char name[8] ;
int age;
char gender;
char department[20] ;
int stuno;
int address;
public :
void output() { }
};
从这两个类的声明中可以看出,它们中有些成员数据和成员函数是相同的。这样就存在代码的重复,为了提高代码的可重用性,就引入了继承。即,如果student类以共有继承的方式继承person类,那么student类就可以直接使用person类中的共有成员如name, gander,age和output() 函数,这样在student类中就不需要再定义与person类中相同的变量了,只定义person类中没有的在student类中需要的变量。具体实现如下:class student : public person // student共有继承person类
{
public :
char department[20] ;
int stuno ;
int address ;
public :
void output() { }
};
提示
student 类称为派生类,person类成为基类;
关键字public 的作用是说明派生类公有继承了基类中的所有成员,即派生类可以直接访问基类的成员;
继承可以使代码得以重用,提高工作效率。
三、派生类访问基类成员
通过前面的学习大家都知道基类的成员有public(公有)、protected(保护)、private(私有)3种访问属性,基类的自身成员可以对基类中任何一个其他成员进行访问,但是通过基类的对象就只能访问该类的公有成员。
派生类可以继承基类中除了构造函数和析构函数之外的全部成员,但是这些成员的访问属性在派生过程中是可以调整的。从基类继承来的成员在派生类中的访问属性是由继承方式控制的。不同的继承方式导致原来具有不同的访问属性的基类成员在派生类的访问属性也有所不同。
在派生类中,从基类继承来的成员可以访问属性有4种:不可直接访问的成员、public (公有成员)、protected(保护成员)、private(私有成员)。
1.三种继承方式的声明
public 公有继承
公有继承:在定义一个派生类时将基类的继承方式指定为public 的继承方式。
例如:class student : public person { }
private 私有继承
私有继承:在定义一个派生类时将基类的继承方式指定为private的继承方式。
例如:class student : private person { }
protected 保护继承
保护继承:在定义一个派生类时将基类的继承方式指定为protected的继承方式。
例如:class student : protected person { }
2.三种继承方式的区别
如表1指明了三种继承方式的区别,即派生类对基类成员的访问能力。
表 1 三种继承方式的区别
在基类中的访问属性继承方式在派生类中的访问属性
private public不可直接访问
private private不可直接访问
private protected不可直接访问
public public public
public private private
public protected protected
protected public protected
protected private private
protected protected protected
提示
在任何继承方式中基类中的私有成员都不允许派生类继承,即在派生类中是不可直接访问基类中的私有成员。
当类的继承方式为公有继承时:基类中的所有公有成员在派生类中仍为公有成员;基类中的所有保护成员在派生类中仍为保护成员。
当类的继承方式为私有继承时:基类中的所有公有成员在派生类中都为私有成员;基类中的所有保护成员在派生类中都为私有成员。
当类的继承方式为保护继承时:基类中的所有公有成员在派生类中都为保护成员;基类中的所有保护成员在派生类中仍为保护成员。
派生类对基类成员的访问形式主要有以下两种:
(1) 内部访问:由派生类中的新增成员对从基类继承来的成员的访问。
(2) 对象访问:在派生类外部,通过派生类的对象对从基类继承的成员的访问。
现在分别讨论在公有继承、私有继承、保护继承这3种继承方式下,派生类对基类成员的访问规则。
一、公有继承
【例1】分析以下程序。
源代码:
#include <>
#include <>
class Base
{
private:
int pvx; //pvx为基类私有变量
protected :
int pty; //pty为基类保护变量
public :
float puf; //puf为基类公有变量
void Setxy(int q, int u, float w) //Setxy()为基类公有函数
{
pvx = q ; pty = u; puf = w;
}
void Output() //Output()为基类公有函数
{
cout << "class Base output "<< endl;
cout << "pvx= " << pvx << endl;
cout << "pty= " << pty << endl;
cout << "puf= " << puf << endl;
}
};
class Derive : public Base //公有单继承
{
private :
float pvz;
public :
void Setvalue(int x, int y, float f, float z)
{
Setxy(x, y, f);
pvx = x; //非法访问,基类中的pvx为private属性,不能被继承
pty = y; //合法访问
puf = f; //合法访问
pvz = z;
}
void Print()
{
cout << "pvx= " << pvx << endl;// 非法访问,在基类中pvx为私有成员
cout << "class Derive output " << endl;
cout << "pty= "< }; void main() { Derive obj; (3,5, , ; (); (); cout << "puf= " << << endl; //合法访问,puf为基类公有成员 cout<<< system("pause"); } 运行输出: class Base output pvx= 3 pty= 5 puf= class Derive output pty= 5 puf= pvz= puf= 【简要分析】 ①“class Derive : public Base { }”表明这是一个公有继承的例子,基类Base中有3个变量和两个函数:私有变量pvx、保护变量pty、公有变量puf和公有函数Setxy()、Output()。 其中:被Derive派生类继承后,pvx在派生类中不能直接访问,只能通过Base中具有public属性的函数对其间接访问;pty被Derive派生类继承之后仍为protected属性,派生类成员可以直接访问;puf、Setxy()、Output()这3个具有public属性成员被Derive派生类继承后,仍为public属性,派生类成员可以对其直接访问。 ②输出的前4行:调用“();”的输出结果。 输出的后4行:调用“();”的输出结果。 最后1行:调用“cout << "puf= " << << endl;”的输出结果。 结论:在公有继承中,无论是派生类中的成员还是派生类对象都不能直接访问基类中的private成员;派生类的成员可以访问继承来的protected成员和public成员;但是,类的外部使用者只能通过派生类的对象访问继承来的public成员,不能访问继承于基类的private 和protected成员。 二、私有继承 【例2】任务1:分析以下程序。 a)找出非法语句并在程序中注释非法的原因。 b)写出删除非法语句后程序的运行结果。 源代码: #include <> #include <> class Base { private: int pvx; protected: int ptf; public : void Setxf(int w, int f) { pvx = w; ptf = f; } void Print() { cout << "pvx= " << pvx << endl; cout << "ptf= " << ptf << endl; } }; class Derive:private Base //私有继承 { private: double pvy ; protected: double ptz; public: int pd; void Setvalue(int a, int b, double c, double d) { pvx = a; ptf = b + 6; Setxf(a, b); pvy = c; ptz = d; pd = 90; } void Output() { cout << "pvx= " << pvx << endl; Print(); cout << "pvy= " << pvy << endl; cout << "ptz= " << ptz << endl; } }; void main() { Derive obj; (1, 6, , ; (); (101, 87); cout << "pvx= " << << endl; cout << "pvy= " << << endl; cout << "ptz= "<< << endl; = 68; cout << "pd= " << << endl; system("pause"); } 【简要分析】 “class Derive :private Base{ }”表明这是一个私有继承,Base基类中有3个变量和两个函数:私有pvx、保护ptf、公有函数Setxf()、公有函数Print()。 基类Base被私有继承之后,其protected成员在派生类中不能直接访问;而protected 成员和public成员都成了派生类的私有成员,只有派生类的成员才能直接访问;派生类对象也不能直接访问继承自Base的protected成员和public成员。 结论:私有继承中,基类中的protected、public成员都成为派生类中的private成员,派生类成员不能直接访问基类中的private成员,而派生类中的成员可以直接访问基类中的protected、public属性的成员;派生类对象不能直接访问基类中的任何成员。 三、保护继承 【例3】任务2:分析以下程序。 a)找出非法语句并在程序中注释非法的原因。 b)写出删除非法语句后程序的运行结果。 源代码: #include <> #include <> class Base { private: int pvx; protected: int pty; public: int puz; void setx(int a) { pvx = a; } int getx() { return pvx; } }; class Derive:protected Base // 保护继承 { private: int pva; protected: int ptb; public: int puc; void setvalue(int a, int b, int c, int d, int e, int f) { pvx = a; setx(a); pty = b; puz = c; pva = d; ptb = e; puc = f; } void print() { cout << "pvx= " << pvx << endl; cout << "pty= " << pty << endl; cout << "puz= " << puz << endl; cout << "pva= " << pva << endl; cout << "ptb= " << ptb << endl; cout << "puc= " << puc << endl; } }; void main() { Derive obj; (9, 8, 7, 6, 5, 4); (); cout << "puc= " << << endl; system("pause"); } 结论:保护继承中,基类中的protected、public成员都成为派生类中的protected成员, 派生类成员不能直接访问基类中的private成员,而派生类中的成员可以直接访问基类中的protected、public属性的成员;派生类对象均不能直接访问基类中的任何成员。 【例4】任务3:分析以下程序。找出非法语句并在程序中注释非法的原因。 分析以下代码。 源代码: #include using namespace std; class person { public : char name[8]; char gender; void show() { cout<< name <<" " << gender <<" " < private: int age; protected: char address[20]; }; class teacher : public person //公有继承 { private : char tecpost[8] ; char department[20]; public: void putout() { cout << name << " " << address << endl; cout << age << endl; cout << department << " " << tecpost << endl; } }; class student : protected person //保护继承{ public: char stuno[15]; char school[20]; void putout() { cout< cout< cout< } }; class doctor : private person //私有继承{ public: char tel[13]; char position[15]; void putout() { cout< cout< cout< } }; void main() { teacher a; student b; doctor c; cout<<< cout<<< cout<<<<" "<<< cout<<< cout<<<<" "<<<<" "<<< cout<<<<" "<<<<" "<<< 实验目的与要求: 1.掌握类的继承与派生关系以及实验方法,理解类的层次结构。 2.掌握派生类构造函数初始化基类成员和对象成员的方法。 3.掌握内联函数和默认函数。 4.掌握赋值兼容原则,掌握派生类的复制构造函数和赋值运算符的定义。 实验过程及内容: 1.实践教程实验二十二P81范例:定义一个继承与派生关系的类体系,在 派生类中访问基类成员。 ①先定义一个点类,包含x,y坐标数据成员,显示函数和计算面积的函数成员; ②以点为基类派生一个圆类,增加表示半径的数据成员,重载显示和计算面积的函数; ③定义一个线段类,以两个点类对象作数据成员,定义显示、求面积及长度函数,线段类采用聚合方式,因为有两个端点,不能用派生。 编程测试所定义的类体系。 本实验教程中有源码,请自行运行,体会和熟悉继承与派生的基本概念及实现方法,掌握派生类构造函数初始化基类成员和对象成员的方法等。2. 实践教程P83编程:多层派生练习,由上题Point类和Circle类继续派生出Cylinder类。要求计算圆柱的底面积、侧面积、全面积和体积。 请编写所有完整的成员函数,并编写主函数进行验证。 数据处理 1. (1) (2)j结果报错,原因是派生类中的成员函数不能访问基类中的私有成员。(3)在Line类中添加两个数据成员。 2. #include 注意派生类的构造函数中对基类数据成员的初始化方法(即 Circle(double xv,double yv,double vv):Point(xv,yv)), 以及构造函数中对对象成员的初始化方法(即 Line(double xv1,double yv1,double xv2,double yv2) : start(xv1,yv1),end(xv2,yv2){ } ) 【要求】 (1)建立工程,录入上述程序,改变数据实验之。 (2)修改Point 类的数据成员x ,y 的访问权限为private ,再运行,结果如何? (3)如果不将Line 类设为 Point 类的友元,应采取什么措施?为哪个类增加数据或函数成员? 2.编程:多层派生练习,由上题Point 类和Circle 类继续派生出Cylinder 类。要求计算圆柱体的底面积、侧面积、全面积和体积。 实验2 派生类与继承 实验课程名:面向对象程序设计(C++) 专业班级:学号:姓名: 实验时间:实验地点:指导教师: 2.1实验目的和要求 (1) 掌握派生类的声明方法和派生类构造函数的定义方法。 (2) 掌握不同继承方式下,基类成员在派生类中的访问属性。 (3) 掌握在继承方式下,构造函数与析构函数的执行顺序与构造规则。 (4) 学习虚基类在解决二义性问题中的作用。 二、实验内容 一、构造一个类Geometry 及其派生类,该类主要实现关于几何图形的基本操作。对于基类“几何图形”,有求面积、求体积的函数(纯虚函数),其派生类圆和矩形主要有初始化(构造函数),求面积,求周长操作,类圆的派生类圆球和圆柱有求表面积、体积操作。 试在主函数中分别定义圆、圆球、圆柱以及矩形的对象,并调用其成员函数实现其相应操作。 实验代码如下: #include ~Geometry(){} double GetArea(){}//求面积函数double GetPerimeter(){}//求体积函数double Getcolume(){}//求周长函数 virtual show(){} }; class Circle:public Geometry { public: Circle(double i) { radiums=i; } ~Circle(){} double GetArea(); double Getcolume(); double R() { return radiums; } show(); private:继承和派生实验报告
c++派生类与继承实验报告材料
实验6-继承与派生(1)