《C++及Windows可视化程序设计 刘振安著》第6章.pptx
《《C++及Windows可视化程序设计 刘振安著》第6章.pptx》由会员分享,可在线阅读,更多相关《《C++及Windows可视化程序设计 刘振安著》第6章.pptx(104页珍藏版)》请在淘文阁 - 分享文档赚钱的网站上搜索。
1、6.7 虚基类实验习题第1页/共104页从已有的对象类型出发建立一种新的对象类型,使它继承原对象类型的特点和功能,这种思想是面向对象设计方法的主要贡献。继承是对许多问题中分层特性的一种自然描述,因而也是类的具体化和被重新利用的一种手段。通过对已有类进行特殊化(派生)来建立新的数据类型,是一种令人激动的新技巧,这种方法赋予面向对象语言以极大的能力和丰富的表现力。第2页/共104页派生在两个层次上建立了一种类属关系。从概念上讲,类的派生创建了一种软件结构,它真实地反映了实际问题。从软件角度来看,类的派生创建了一种类族。派生类的对象也是基类的一种对象,它可以被用在基类对象所使用的任何地方。可以用多态
2、成员函数仔细调整这种关系,以便使派生类在某些地方与它的基类一致,而在别的地方表现出它自身的行为特征。本章主要讨论C+语言继承方面的语法特征和一般的使用方法。第3页/共104页从一个或多个以前定义的类(基类)产生新类的过程称为派生,这个新类又称为派生类。类的继承是指新类从基类那里得到基类的特征,也就是继承基类的数据和函数。派生的新类同时也可以增加或重新定义数据和操作,这就产生了类的层次性。派生就是创建一个具有别的类的属性和行为的新类的能力。派生和继承的概念也来自于人们认识客观世界的过程。6.1 继承和派生的基本概念第4页/共104页举个简单的例子:“狗”和“黑狗”。当谈论“狗”的时候,知道它是哺
3、乳动物,有4条腿,1条尾巴,喜欢啃肉骨头,现在谈论“黑狗”,人们会怎么说呢?一种是说“黑狗是一种哺乳动物,有4条腿,1条尾巴,喜欢吃肉骨头,并且它的毛是黑色的”。另一种是说:“黑狗就是黑毛狗”。比较一下这两种说法,显然后一种说法更好。那么它好在哪里呢?第一,它更简练;第二,更重要的是它反映了“狗”和“黑狗”这两个概念的内在联系。“狗”和“黑狗”之间存在一条重要的联系,那就是所有的“黑狗”都是“狗”,或者说,“黑狗”是一类特殊的“狗”。根据这一条,“狗”所具有的特征,例如4条腿,1条尾巴等,“黑狗”自然都具有。也就是说,“黑狗”从“狗”那里继承了“狗”的全部特征。第5页/共104页现在我们可以用
4、C+语言来描述这一问题。显然可以定义一个描述“狗”的类dog。但是描述“黑狗”的类blackdog怎么办?为了能准确地描述这两个类之间的关系,C+提供了一种机制,使得人们可以像“黑狗就是黑毛狗”那样定义一个新类blackdog。在这种机制下,类blackdog自动拥有类dog 的所有成员,该类的每一个对象都是类dog 的对象,也就是说,“每一条黑狗都是狗”。这种机制的具体实现留到下一节再说。这一节的主要任务是弄清楚基本概念。现在先来看几个术语。第6页/共104页“黑狗是黑毛的狗”是从一般的dog类通过特殊化而得到类blackdog的。这种通过特殊化已有的类来建立新类的过程,叫做“类的派生”,原
5、有的类叫做“基类”,新建立的类则叫做“派生类”。这里类dog 就是基类,而blackdog是派生类。从类的成员的角度看,派生类自动地将基类的所有成员作为自己的成员,这叫做“继承”。基类和派生类又可以分别叫做“父类”和“子类”,有时也称为“一般类”和“特殊类”。类的派生和继承是面向对象程序设计方法和C+语言最重要的特征之一。第7页/共104页客观世界本身是有层次的,人们认识客观世界的过程中,由一般到特殊的演绎思维发挥着巨大作用。演绎的过程在绝大多数情况下就表现为层次分类的过程。继承使得程序员可以在一个较一般的类的基础上很快地建立一个新类,而不必从零开始设计每个类。继承常用来表示类属关系,不能将继
6、承理解为构成关系。当从现存类中派生出新类时,可以对派生类做如下几种变化:可以增加新的数据成员;可以增加新的成员函数;可以重新定义已有的成员函数;可以改变现有成员的属性。第8页/共104页如图6.1所示,C+中有两种继承:单一继承和多重继承。单一继承,派生类只能有一个基类。多重继承,派生类可以有多个基类。图6.1类的单一继承和多重继承的UML结构图第9页/共104页在图6.1中,箭头指向基类。单一继承形成一个倒挂的树,多重继承形成一个有向无环图。派生类继承了基类所有的数据成员和成员函数,程序员也可以在派生类中添加新的数据成员和成员函数。这样,基类定义了对象的一个集合,派生类通过增添新的成员限制该
7、定义,以便定义这个集合的一个子集。从编码角度讲,派生类从基类中以较低代价换来了大量的灵活性。一旦产生了可靠的基类,只需要调试派生类中所做的修改即可。C+派生类从父类中继承性质时,可使派生类扩展它们,或者对其做些限制,也可改变或删除,甚至不作任何修改。所有这些变化可归结为两类基本的面向对象技术。第一种称为性质约束,即对基类的性质加以限制或删除。第二种称为性质扩展,即增加派生类的性质。第10页/共104页在C+中,声明单一继承的一般形式为:class 派生类名 :访问控制 基类名 private:成员声明列表 protected:成员声明列表 public:成员声明列表;6.2 单一继承 6.2.
8、1 单一继承的一般形式第11页/共104页这里和一般的类的声明一样,用关键字class 声明一个新的类。冒号后面的部分指示这个新类是哪个基类的派生类。“访问控制”是指如何控制基类成员在派生类中的访问属性,它是3个关键字public,protected和private 中的一个。一对大括号“”中是用来声明派生类自己的成员的。这和类的声明一样,不再赘述。第12页/共104页如何初始化派生类的对象呢?当然也应在派生类中声明一个与派生类同名的函数,即它的构造函数。假设从基类Point派生一个描述矩形的类Rectangle。类Rectangle继承Point类的两个数据成员作为矩形的一个顶点,再为Rec
9、tangle类增加两个数据成员H 和W,分别描述它的高和宽。为类Point设计一个构造函数Point(int,int)和显示数据的函数Showxy。为Rectangle类也设计构造函数Rectangle(int,int,int,int)和显示函数Show。派生类增加了两个新的数据成员以及相应的成员函数,同时继承Point的全部成员。【例6.1】是它们的程序实现。6.2.2 派生类的构造函数和析构函数第13页/共104页【例6.1】使用默认内联函数实现单一继承。#includeusing namespace std;class Point private:int X,Y;public:Point
10、(int a,int b)X=a;Y=b;coutPoint.endl;void Showxy()coutX=X,Y=Yendl;Point()coutDelete Pointendl;第14页/共104页class Rectangle:public Point private:int H,W;public:Rectangle(int a,int b,int h,int w):Point(a,b)/构造函数初始化列表H=h;W=w;coutRectangle.endl;void Show()coutH=H,W=Wendl;Rectangle()coutDelete Rectangleendl;
11、void main()Rectangle r1(3,4,5,6);r1.Showxy();/派生类对象调用基类的成员函数第15页/共104页 r1.Show();/派生类对象调用派生类的成员函数程序输出如下:Point./调用基类构造函数Rectangle./调用派生类构造函数X=3,Y=4 /调用基类成员函数Showxy()H=5,W=6 /调用派生类成员函数Show()Delete Rectangle/调用派生类析构函数Delete Point /调用基类析构函数在派生类中继承的基类成员的初始化,需要由派生类的构造函数调用基类的构造函数来完成,这和初始化对象成员有类似之处。第16页/共10
12、4页定义派生类的构造函数的一般形式为:派生类名 派生类名(参数表0):基类名(参数表)/函数体 冒号后“基类名(参数表)”称为成员初始化列表,参数表给出所调用的基类构造函数所需要的实参。实参的值可以来自“参数表0”,或由表达式给出。可以像Rectangle那样,在类中直接定义为内联函数。下面是在类说明之外定义的示范:RectangleRectangle(int a,int b,int h,int w):Point(a,b)H=h;W=w;coutRectangle.endl;第17页/共104页“参数表0”有4个参数,基类Point的参数表是自己的2个数据成员。当定义派生类的一个对象时,首先调
13、用基类的构造函数,对基类成员进行初始化,然后执行派生类的构造函数,如果某个基类仍是一个派生类,则这个过程递归进行。当该对象消失时,析构函数的执行顺序和执行构造函数时的顺序正好相反。输出结果也证实了这个结论。第18页/共104页如果想修改Rectangle 的Show函数,使得它可以一次显示X、Y、H 和W。怎样修改成员函数Show呢?修改成下面的内容能实现这一目的吗?void Rectangleshow()coutX=X,Y=Y ,H=H,W=W endl;6.2.3 类的保护成员第19页/共104页这段简单程序并不能通过编译。类Rectangle有4个私有成员X、Y、H 和W。这4个私有成员
14、的来源是不一样的。H 和 W是Rectangle 自己定义的,而X 和Y 是从Point那里继承来的。换句话说,X和Y是类Point的私有成员。类的私有成员是只能被它自己的成员函数(不讨论友元)访问的,而Show函数是在类Rectangle 中定义的,它是类Point子类的成员函数,并不是类Point的成员函数,因而不能访问X 和Y。第20页/共104页C+语言规定,公有派生类的成员函数可直接访问基类中定义的或基类(从另一个基类)继承来的公有成员,但不能访问基类的私有成员。这和私有成员的定义是一致的,符合数据封装思想。但这样也有问题,就拿上面的程序来说,在类Rectangle 看来,X、Y、H
15、和W的地位是平等的,现在希望对它们“一视同仁”,但C+语言关于私有成员继承的规定却妨碍这样做。为解决这一矛盾,C+引入了保护成员的概念。第21页/共104页在类声明中,关键字protected之后声明的是类的保护成员。保护成员具有私有成员和公有成员的双重角色:对派生类的成员函数而言,它是公有成员,可以被访问;而对其他函数而言则仍是私有成员,不能被访问。要想在类Rectangle 中使用统一的Show函数,只要把X 和Y 定义成类Point的保护成员就行了。【例6.2】是对【例6.1】修改过的程序。第22页/共104页【例6.2】演示使用protected成员。#include using na
16、mespace std;class Point protected:int X,Y;public:Point(int a,int b)X=a;Y=b;void Show()/基类的Show()函数 coutX=XY=Yendl;class Rectangle:public Point private:int H,W;public:第23页/共104页Rectangle(int,int,int,int);/声明构造函数原型void Show()coutX=X,Y=Y,H=H,W=WShow();/实际调用的是基类的Show函数 Rectangle*pb=&b;/派生类指针pb pb-Show()
17、;/调用派生类的Show函数 a=b;/派生类对象的属性值更新基类对象的属性值 a.Show();X=1,Y=2/用a的Show函数输出对象a的数据成员X=3,Y=4,H=5,W=6/用b的Show函数 /输出对象b的数据成员X=3,Y=4/用b的基类Show函数输出其基类数据成员X=3,Y=4/用b的基类Show函数输出其基类数据成员X=3,Y=4,H=5,W=6/b的指针调用Show函数 /输出对象b的数据成员X=3,Y=4/用a的Show函数输出对象a的数据成员第31页/共104页为什么“p-Show();”不是使用b的Show函数输出“X=3,Y=4,H=5,W=6”?因为“base*
18、pb=&d;”显式地指出指针是基类base的指针,所以编译系统对它采取静态联编,将指针指向基类,调用的是基类的成员函数Show。只有派生类的指针才能调用派生类的成员函数,引用的情况也是如此,程序的输出结构也证实了这一点。对于“a=b;”,从输出结果可以看出,它们的含义就是基类用派生类的属性值代替自己原来的属性值。这就是公有派生的“isa”原则。第32页/共104页2.“isa”和“has-a”的区别类与类之间的关系有两大类。一是继承和派生问题,二是一个类使用另一个类的问题。后者的简单用途是把另一个类的对象作为自己的数据成员或者成员函数的参数。继承首先要掌握公有继承的赋值兼容规则,理解公有继承“
19、就是一个(isa)”的含义。第33页/共104页公有继承的赋值兼容规则如果写成类B公有继承于类A,类B的每一个对象也是一个类A的对象,但反之则不然。如果说对于一个类A的对象是正确的一件事,对于一个类B的对象同样也是真实的,但反之则不然。如果说A表示一个比B更普遍的概念,也就是B表示一个比A更特殊的概念。可以断言:在可以使用类A的对象的任何地方,则类B的对象同样也能使用,因为每一个类B的对象“就是一个”类A的对象。另一方面,如果需要一个类B的对象,则类A的对象就不行;每个B都是A,但反之则不然。C+强制执行公有继承的这种解释。例如:第34页/共104页 class Person.;class S
20、tudent:Public Person.;这两个类所断言的是:每个学生都是人,但并不是每个人都是学生。可以期望任何一件对于人来说是真实的事情,对学生也是真实的,例如他或她都有生日,对于学生同样也是真实的。但却不能期望每一件对于学生来说是真实的事情,对所有的人都是真实的。譬如说他或她就读于一所指定的学校,这对于一般的人就不能都是真实的。“人”的概念要比“学生”的概念来得更广泛些;而“学生”则是一种特殊类型的“人”。第35页/共104页在C+范围内,任何一个要求提供Person类型(或指向Person的指针及引用)参数的函数,也能够使用Student对象(或指向Student的指针及引用)作为参
21、数。例如void dance(const Person&p);/任何人都能跳舞void study(const Student&s);/只有学生要学习Person p;/p是PersonStudent s;/s是Studentdance(p);/对,p是Persondance(s);/对,s是Student,student也是(isa)Personstudy(s);/对study(p);/错!p不都是Student第36页/共104页这只是对公有继承才是正确的。仅当Student类是从Person类中公有地派生出来的时候,C+才是像刚才所描述的那样。公共继承和“isa”的等价性看起来很简单,但
22、在实际应用中并不容易。有时自己的直觉会产生误导。譬如说,企鹅是鸟是一件事,鸟会飞是另一件事。如果已有一个address类,它描述地址这个概念,现在要建立一个worker类,它描述职工这个概念,每个职工都有一个住址,worker类可以有两种定义形式:使用继承或对象成员。如果采用继承,可以按如下形式定义:class worker:public address /;第37页/共104页如果在worker类中定义一个address类的类对象成员,可以按如下形式定义:class worker private address workerAddr;/;使用继承的方法是说职工是一个地址,使用对象的方法声称职
23、工包含有一个地址属性。这两种说法中后者的说法是正确的,即地址只能作为职工的一个属性,在概念上,它们之间没有联系,所以上例中第2种的描述形式是合理的。这就是分层实现。第38页/共104页由此可见,类用于描述一类对象的共同特性,不同种类的对象之间的联系使用继承来表示,对象所具有的属性使用类的成员来表示。分层就是一种处理过程,它通过让分层的类里包含被分层的类的对象作为其数据成员,以便把一个类建立在另一些类之上。例如:class String;/字符串class Address;/某人居住的地方class PhoneNumber;第39页/共104页class Person private:Strin
24、g name;/被分层的对象Address address;/被分层的对象PhoneNumber voiceNumber;/被分层的对象PhoneNumber faxNumber;/被分层的对象 public:;Person类要被分层到String、Address和PhoneNumber这几个类的上面,这是因为它里面含有这些类型的数据成员。第40页/共104页分层也可以叫做包含、嵌入或者聚合。公有继承的意思是“isa”。与此相反,分层的意思是指“has-a(有一个)”或者“is-implemented-in-terms-of(是按实现的)”。上面的Person类表示的是一种“has-a”的关系
25、。一个Person对象有一个名字、一个地址和用于语音及传真通信的两个电话号码。我们不会说一个人就是一个字符串,或者一个人就是一个地址,但会说一个人有一个字符串,并且有一个地址等。许多人做这种分辨时困难不大,搞不清“isa”和“has-a”之间区别的人相对来说也很少见。稍微有些伤脑筋的是“isa”和is-implemented-in-terms-of之间的差异,这可通过例子来加深理解。第41页/共104页3.公有继承存取权限表派生类一般都使用公有继承。使用基类的有基类本身、派生类、对象和外部函数。对派生类而言,使用它的有派生类本身、对象和外部函数。类中可以使用3种成员,它们之间的关系如下表所示。
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- C+及Windows可视化程序设计 刘振安著 C+及Windows可视化程序设计 刘振安著第6章 C+ Windows 可视化 程序设计 刘振安著
限制150内