第七章 继承和派生
合集下载
相关主题
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
在派生类中继承的基类成员的初始化,需要由派生 类的构造函数调用基类的构造函数来完成。 定义派生类的构造函数的一般形式为: 派生类名 :: 派生类名(参数表0) : 基类名(参数表) {.„..//函数体 } 冒号后“基类名(参数表)”称为成员初始化列表,参数 表给出所调用的基类构造函数所需要的实参。实参的值 可以来自“参数表0”,或由表达式给出。 可以像Rectangle那样,在类中直接定义为内联函数。 下面是在类说明之外定义的示范: Rectangle::Rectangle(int a, int b, int h, int w):Point(a,b) {H=h; W=w; cout<<"Rectangle..."<<endl;} “参数表0”有4个参数,基类Point的参数表是自己的2 个数据成员。
Байду номын сангаас
从一个或多个以前定义的类(基类)
产生新类的 过程称为派生,这个新类称为派生类。派生的新 类同时也可以增加或重新定义数据和操作,这就 产生了类的层次性。 类的继承是指新类继承基类的成员。继承常用来 表示类属关系,不能将继承理解为构成关系。当 从现存类中派生出新类时,可以对派生类做如下 几种变化: ① 可以增加新的数据成员; ② 可以增加新的成员函数; ③ 可以重新定义已有的成员函数; ④ 可以改变现有成员的属性。
如图7.1所示,C++中有两种继承:单一继承和多 重继承。对于单一继承,派生类只能有一个基类; 对于多重继承,派生类可以有多个基类。
图7.1 类的单一继承和多重继承的UML结构图
在图7.1中,箭头指向基类。单一继承形成一个
倒挂的树。派生类继承了基类所有的数据成员和 成员函数,程序员也可以在派生类中添加新的数 据成员和成员函数。 C++派生类从父类中继承性质时,可使派生类扩 展它们,或者对其做些限制,也可改变或删除, 甚至不作任何修改。所有这些变化可归结为两类 基本的面向对象技术。 第一种称为性质约束,即对基类的性质加以限制 或删除。 第二种称为性质扩展,即增加派生类的性质。
class Rectangle : public Point { private: int H, W; public: Rectangle(int, int, int, int); //构造函数原型 void Show() { cout<<"x="<<x<<",y="<<y<<",H="<<H <<",W="<<W<<endl; } }; //定义构造函数 Rectangle::Rectangle(int a, int b, int h, int w):Point(a,b) {H=h; W=w;}
void main() { Point a(3,4); Rectangle r1(3,4,5,6); a.Show(); //基类对象调用基类Show()函数 r1.Show(); //派生类对象调用派生类Show()函数 } 派生类虽然继承了基类的成员函数Show,但它改造 了这个函数,使它能显示所有数据。这并不会影响基 类函数原来的功能。程序有意定义了Point的对象a, 执行a.Show()验证之。 程序输出如下: x=3,y=4 x=3,y=4,H=5,W=6
第7章
继承和派生
从已有的对象类型出发建立一种新的对象类型, 使它继承原对象类型的特点和功能,这种思想是 面向对象设计方法的主要贡献。
主要内容
7.1 继承和派生的基本概念 7.2 单一继承 7.3 多重继承 7.4 二义性及其支配规则 7.5 设计实例
7.1 继承和派生的基本概念
可以定义一个描述“狗”的类dog
程序输出如下: Point... //调用基类构造函数 Rectangle... //调用派生类构造函数 x=3,y=4 //调用基类成员函数Showxy() H=5,W=6 //调用派生类成员函数Show() Delete Rectangle //调用派生类析构函数 Delete Point //调用基类析构函数
7.2.3 类的保护成员
现在修改Rectangle 的Show函数,使得它可以 一次显示x 、y 、H 和W 。怎样修改成员函数Show 呢? void Rectangle::show(){ cout<< "x=" <<x <<",y="<<y<< ",H=" << H <<",W="<<W<<endl; } 这段程序并不能通过编译。x和y是类Point的私 有成员。类的私有成员是只能被它自己的成员函数 (不讨论友元)访问的,而Show函数是在类Rectangle 中定义的,它是类Point子类的成员函数,并不是类 Point的成员函数,因而不能访问x 和y 。
public: Rectangle(int a, int b, int h, int w):Point(a,b) //构造函 //数初始化列表 {H=h; W=w; cout<<"Rectangle..."<<endl;}; void Show(){cout<<"H="<<H<<",W="<<W<<endl;} ~Rectangle(){cout<<"Delete Rectangle"<<endl;} }; void main() { Rectangle r1(3,4,5,6); r1.Showxy(); //派生类对象调用基类的成员函数 r1.Show(); //派生类对象调用派生类的成员函数 }
“每一只黑狗都是狗”,每一个派生类的对象都是基 类的一个对象,于是可以得出赋值兼容规则:所谓赋值 兼容规则是指在公有派生情况下,一个派生类的对象可 以作为基类的对象来使用的情况。 约定类derived 是从类base公有派生而来的,则包含 如下3种情况: ① 派生的对象可以赋给基类的对象。例如: derived d; base b; b = d; ② 派生类的对象可以初始化基类的引用。例如: derived d; base& br = d; ③ 派生类的对象的地址可以赋给指向基类的指针。例如: derived d; base* pb = &d; 但要注意,在后两种情况下,通过pb或br只能访问对象 d 中所继承的基类成员。
【例7.2】演示使用protected成员。 #include <iostream> using namespace std; class Point { protected: int x,y; public: Point(int a, int b){x=a; y=b;} void Show(){cout<<“x=”<<x<<“,y=”<<y<<endl;}; //基类 //的Show()函数 };
C++语言规定,公有派生类的成员函数可直接 访问基类中定义的或基类(从另一个基类)继承来 的公有成员,但不能访问基类的私有成员。为解决 这一矛盾,C++引入了保护成员的概念。 在类声明中,关键字protected之后声明的是类 的保护成员。保护成员具有私有成员和公有成员的 双重角色: 对派生类的成员函数而言,它是公有成员,可以 被访问; 而对其他函数而言则仍是私有成员,不能被访问。
【例7.3】使用Point和Rectangle类演示赋值兼容规则的例子。 void main( ) { //表演公有继承的赋值兼容性规则 Point a(1,2); //基类对象a Rectangle b(3,4,5,6); //派生类对象b a.Show(); b.Show(); Point& ra=b; //派生类对象初始化基类的引用 ra.Show(); //实际调用的是基类的Show函数 Point* p=&b; //派生类对象的地址赋给指向基类的指针 p->Show(); //实际调用的是基类的Show函数 Rectangle* pb=&b; //派生类指针pb pb->Show(); //调用派生类的Show函数 a=b; //派生类对象的属性值更新基类对象的属性值 a.Show(); }
7.2.2 派生类的构造函数和析构函数
假设从基类Point派生一个描述矩形的类 Rectangle 。类Rectangle继承Point类的两个数据 成员作为矩形的一个顶点,再为Rectangle类增加 两个数据成员H 和W,分别描述它的高和宽。 为类Point设计一个构造函数Point(int,int)和 显示数据的函数Showxy。为Rectangle类也设计 构造函数Rectangle(int,int, int, int)和显示函数 Show。由此可见,派生类增加了两个新的数据成 员以及相应的成员函数,同时继承Point的全部成 员。 【例7.1】是它们的程序实现。
7.2 单一继承
7.2.1 单一继承的一般形式
在 C++中,声明单一继承的一般形式为: class 派生类名:访问控制 基类名{ private: 成员声明列表 protected: 成员声明列表 public: 成员声明列表 }; 这里和一般的类的声明一样,用关键字class声明一个新的类。 冒号后面的部分指示这个新类是哪个基类的派生类。所谓“访问 控制”是指如何控制基类成员在派生类中的访问属性,它是3个关 键字public, protected和private 中的一个。 一对大括号“{ }”中是用来声明派生类自己的成员的。
【例7.1】使用默认内联函数实现单一继承。
#include <iostream> using namespace std; class Point { private: int x,y; public: Point(int a, int b){x=a; y=b; cout<<"Point..."<<endl;} void Showxy(){cout<<"x="<<x<<",y="<<y<<endl;} ~Point(){cout<<"Delete Point"<<endl;} }; class Rectangle : public Point { private: int H, W;
所谓“不可访问”,是说一个成员甚至对于 其自身所在类的成员来说也是不可访问的。 在根类(不是从别的类派生出来的类)中,没 有成员是不可访问的。对于根类来说,可能的访 问级别是 private 、public和protected 。但是在派 生类中,可以存在第4种访问级别:不可访问 (inaccessible)。 不可访问成员总是由基类继承来的,要么是基 类的不可访问成员,要么是基类的私有成员。当 希望类的某些成员能够被子类所访问,而又不能 被其他的外界函数访问的时候,就应当把它们定 义为保护的。千万不能把它们定义成私有的,否 则在子类中它们就会是不可访问的。
。但是描述“黑 狗”的类blackdog怎么办?为了能准确地描述这两个 类之间的关系,C++提供了一种机制,使得人们可以 像“黑狗就是黑毛的狗”那样定义一个新类blackdog。 在这种机制下,类blackdog自动拥有类dog的所有成 员,该类的每一个对象都是类dog 的对象,也就是说, “每一条黑狗都是狗”。 “黑狗是黑毛的狗”是从一般的dog类通过特殊化而 得到类blackdog的。这种通过特殊化已有的类来建立 新类的过程,叫做“类的派生”,原有的类叫做“基 类”,新建立的类则叫做“派生类”。这里类dog就 是基类,而blackdog是派生类。 另一方面,从类的成员的角度看,派生类自动地将 基类的所有成员作为自己的成员,这叫做“继承”。 基类和派生类又可以分别叫做“父类”和“子类”, 有时也称为“一般类”和“特殊类”。
7.2.4 访问权限和赋值兼容规则
1. 公有派生和赋值兼容规则
在前面的例子中,使用了公有派生。在公有派生的情况下, 基类成员的访问权限在派生类中保持不变。这就意味着在 程序中: ① 基类的公有成员在派生类中仍然是公有的。 ② 基类的保护成员在派生类中仍然是保护的。 ③ 基类的不可访问的和私有的成员在派生类中也仍然是 不可访问的。
构造函数(包括析构函数)是不被继承的,所 以一个派生类只能调用它的直接基类的构造函 数。 当定义派生类的一个对象时,首先调用基类的 构造函数,对基类成员进行初始化,然后执行 派生类的构造函数,如果某个基类仍是一个派 生类,则这个过程递归进行。 当该对象消失时,析构函数的执行顺序和执行 构造函数时的顺序正好相反。