可视化编程技术(3).ppt
C+基础C+面向对象程序设计基础3.1.2 面向对象程序设计方法及特征面向对象程序设计方法及特征n n什么是对象:什么是对象:什么是对象:什么是对象:n n现实世界是由各种各样的事物组成,包括真实的事物和抽现实世界是由各种各样的事物组成,包括真实的事物和抽象的事物。例如,人、动物、汽车象的事物。例如,人、动物、汽车(真实的事物真实的事物)和程序、和程序、直线直线(抽象的事物抽象的事物)等。等。n n每一类事物都有自己特定的属性每一类事物都有自己特定的属性(如大小、形状、重量等如大小、形状、重量等)和行为和行为(如生长、行走、转弯、运算等如生长、行走、转弯、运算等),人们通过研究事,人们通过研究事物的属性和行为而认识事物。物的属性和行为而认识事物。n n在计算机科学中将这些现实世界中的事物称之为在计算机科学中将这些现实世界中的事物称之为对象对象对象对象。对。对象是包含现实世界中事物特征的抽象实体,它反映了系统象是包含现实世界中事物特征的抽象实体,它反映了系统为之保存信息和与之交互的方法。为之保存信息和与之交互的方法。n n在程序设计领域,可以用如下公式表示:在程序设计领域,可以用如下公式表示:n n 对象对象 =数据数据 +作用于这些数据上的操作作用于这些数据上的操作什么是类:什么是类:n n为了描述属性和行为相同的一类对象,引入了类(class)的概念。n n类是具有相同数据结构(属性)和相同操作功能(行为)的对象的集合,它规定了这些对象的公共属性和行为方法。n n对象是类的一个实例,例如,汽车是一个类,而行驶在公路上的一辆汽车则是一个对象。n n对象和类的关系相当于程序设计语言中变量和变量类型的关系。一个简单例子:一个简单例子:class Time class Time private:private:intint hour;hour;/数据成员,表示小时数据成员,表示小时 intint minute;minute;/数据成员,表示分钟数据成员,表示分钟 intint second;second;/数据成员,表示秒数据成员,表示秒public:public:void void setTime(intsetTime(int h,h,intint m,m,intint s)/s)/成员函数,设置时间成员函数,设置时间 hour=(h=0&h=0&h=0&m=0&m=0&s=0&s60)?s:0;void void showTimeshowTime()()/成员函数,输出时间成员函数,输出时间 coutcouthour:minute:secondhour:minute:secondendlendl;main()main()Time Time EndTimeEndTime;/声明对象声明对象EndTimeEndTime /设置对象设置对象EndTimeEndTime的时间的时间(属性,数据成员属性,数据成员)EndTime.setTime(12,23,36);EndTime.setTime(12,23,36);coutcoutThe time is:;The time is:;/显示对象显示对象EndTimeEndTime的时间的时间 EndTime.showTimeEndTime.showTime();();运行结果:运行结果:运行结果:运行结果:The time isThe time is:12:23:3612:23:36面向对象程序设计方法的基本特征面向对象程序设计方法的基本特征面向对象程序设计方法具有四个基本特征:面向对象程序设计方法具有四个基本特征:n n抽象抽象n n封装封装n n继承继承n n多态性多态性1.1.抽象抽象抽象抽象 抽象是人类认识问题的最基本手段之一。抽抽象是人类认识问题的最基本手段之一。抽象是指对具体问题象是指对具体问题(对象对象)进行概括,抽出一进行概括,抽出一类类类类对对象的公共属性和行为并加以描述的过程。抽象包象的公共属性和行为并加以描述的过程。抽象包括数据抽象和代码抽象括数据抽象和代码抽象(或行为抽象或行为抽象)。2.2.封装封装封装封装 封装是把每个对象的数据封装是把每个对象的数据(属性属性)和操作和操作(行为行为)包装在一个类中。一旦定义了对象的属性和行为,包装在一个类中。一旦定义了对象的属性和行为,则必须决定哪些属性和行为只用于表示内部状态,则必须决定哪些属性和行为只用于表示内部状态,哪些属性和行为在外部是可见的。哪些属性和行为在外部是可见的。一般限制直接访问对象的属性,而应通过操作一般限制直接访问对象的属性,而应通过操作接口访问,这样使程序中模块之间关系更简单、接口访问,这样使程序中模块之间关系更简单、数据更安全。对程序的修改也仅限于类的内部,数据更安全。对程序的修改也仅限于类的内部,使得由于修改程序所带来的影响局部化。使得由于修改程序所带来的影响局部化。n n3.3.继承继承继承继承n n 继承是指一个新类可以从现有的类派生而来。继承是指一个新类可以从现有的类派生而来。新类继承了现有类的特性,包括一些属性和行为,新类继承了现有类的特性,包括一些属性和行为,并且可以修改或增加新的属性和行为,使之适合并且可以修改或增加新的属性和行为,使之适合具体的需要。具体的需要。n n 例如,所有的例如,所有的WindowsWindows应用程序都有一个窗应用程序都有一个窗口,它们可以看作都是从一个窗口类派生出来的,口,它们可以看作都是从一个窗口类派生出来的,但有的应用程序用于文字处理,有的应用程序用但有的应用程序用于文字处理,有的应用程序用于绘图,这是由于派生出了不同的类,它们增加于绘图,这是由于派生出了不同的类,它们增加了不同的属性和行为。了不同的属性和行为。n n 继承很好地解决了软件的可重用性问题。继承很好地解决了软件的可重用性问题。n n4.4.多态性多态性多态性多态性n n 多态性是指类中具有相似功能的不同函数使多态性是指类中具有相似功能的不同函数使用同一个名称来实现,并允许不同类的对象对同用同一个名称来实现,并允许不同类的对象对同一消息作出的响应不相同。一消息作出的响应不相同。n n 例如,同样的例如,同样的“编辑编辑|粘贴粘贴”操作,在字处操作,在字处理程序和绘图程序中有不同的结果;同样的加法,理程序和绘图程序中有不同的结果;同样的加法,把两个时间值相加和把两个整数相加的要求肯定把两个时间值相加和把两个整数相加的要求肯定不同。不同。n n 多态性使程序设计灵活、抽象,具有行为共多态性使程序设计灵活、抽象,具有行为共享和代码共享的优点,很好地解决了程序的函数享和代码共享的优点,很好地解决了程序的函数同名问题。同名问题。3.2 C+类类n n为了支持面向对象程序设计,为了支持面向对象程序设计,C+C+在在C C语言结构语言结构(structstruct)数据类型的基础上引入了)数据类型的基础上引入了类类类类这种抽象数这种抽象数据类型。据类型。n nC+C+面向对象编程实质上就是面向面向对象编程实质上就是面向类类类类编程,只有编程,只有定义和实现了类,才能声明属于这个类的对象,定义和实现了类,才能声明属于这个类的对象,才能通过对象使用定义的成员。才能通过对象使用定义的成员。n n传统传统C C程序员把编程重点放在函数的编写上,而程序员把编程重点放在函数的编写上,而C+C+程序员把重点放在程序员把重点放在类类类类的定义和实现上。的定义和实现上。3.2.1 类的定义与实现类的定义与实现n nC+类将对象的属性抽象为数据成员,将对象的行为抽象为成员函数,并对它们进行封装。数据成员又称成员变量,成员函数又称为方法。n nC+类在形式上类似于C语言中用户自定义的结构类型,但定义类时规定了成员的访问控制权限。对象只能访问所属类的公有成员,而类的私有成员只能在类的成员函数中被访问。C+类定义的基本形式:类定义的基本形式:class class privateprivate:;publicpublic:;protectedprotected:;说明:说明:n n类的定义由关键字class开始,其后为用户定义的类名,花括号括起来的部分称为类体。n n关键字private、public和protected称为访问权限控制符,用来设置数据成员和成员函数的访问属性,其默认值为private。n nprivate属性表示数据成员和成员函数是类的私有成员,它们只允许被本类的成员函数访问或调用,数据成员一般定义为private属性;n npublic属性表示数据成员和成员函数是类的公有成员,它们允许被本类或其它类的成员函数(通过对象)访问或调用,是类的外部接口,成员函数一般定义为public属性;n nprotected属性表示数据成员和成员函数是类的保护成员,它们允许被本类的成员函数和派生类的成员函数访问或调用。例例 定义类Time(表示时间)。class Timeclass Timeprivate:private:/最好不要省略最好不要省略privateprivate intint hour;hour;/数据成员,表示小时数据成员,表示小时 intint minute;minute;/数据成员,表示分钟数据成员,表示分钟 intint second;second;/数据成员,表示秒数据成员,表示秒public:public:void void setTime(intsetTime(int,intint,intint);/);/成员函数,设置时成员函数,设置时间间 void void showTimeshowTime();();/成员函数,输出时间成员函数,输出时间;类的实现:类的实现:n n利用C+类进行面向对象编程,定义类的成员只是完成了工作的第一步,最重要的工作是实现定义的类。n n类的实现实质上是类的成员函数的实现,即定义类的成员函数。n n成员函数的定义形式与一般函数的定义形式基本相同,但必须在成员函数名前加上类名和作用域限定符(:)。n n成员函数的定义也可放在类体内(该函数声明之处),这时成员函数将变成内联函数。例例 类Time的实现。void void Time:setTime(intTime:setTime(int h,h,intint m,m,intint s)s)hour=(h=0&h=0&h=0&m=0&m=0&s=0&s60)?s:0;void void Time:showTimeTime:showTime()()coutcouthour:minute:secondhour:minute:second-”访问对象的公有成员,但不能访问访问对象的公有成员,但不能访问对象的私有成员。对象的私有成员。n n 例如,公有成员函数调用:例如,公有成员函数调用:t1.setTime()t1.setTime();start.showTimestart.showTime()();pt1-pt1-setTimesetTime()();n n 而任何形如而任何形如t1.hourt1.hour、t1.minutet1.minute、start.secondstart.second等私有成员变量的直接访问都是非法的。等私有成员变量的直接访问都是非法的。例:类成员的访问属性n nTime endtime;endtime.hour=3;/非法 h=endtime.hour;/非法 正确的访问方法:endtime.setTime(3,);endtime.showTime();n n例例例例 类类TimeTime的使用,声明对象并设置对象属性。的使用,声明对象并设置对象属性。main()main()Time Time EndTimeEndTime;/声明对象声明对象EndTimeEndTime EndTime.setTime(12,23,36);EndTime.setTime(12,23,36);/设置对象设置对象EndTimeEndTime的时间的时间 coutcoutThe time is:;The time is:;EndTime.showTimeEndTime.showTime();();/显示对象显示对象EndTimeEndTime的时间的时间 3.2.2 构造函数和析构函数构造函数和析构函数n n在定义类时不能对成员变量进行初始化,因为无法确定成员变量属于哪一个对象。n n成员变量一般都定义为私有属性,也不能在声明对象后利用赋值运算对成员变量进行初始化。n n成员变量的初始化一般是利用一个名为构构造函数造函数的成员函数来完成。n n什么是构造函数:什么是构造函数:构构造造函函数数是是一一种种特特殊殊的的成成员员函函数数,它它是是在在创创建建对对象象时时(声声明明或或newnew动动态态创创建建)系系统统自自动动调调用用的的成员函数。成员函数。什么是析构函数:什么是析构函数:析析构构函函数数也也是是一一种种特特殊殊的的成成员员函函数数,它它是是在在对对象生存期结束时系统自动调用的成员函数。象生存期结束时系统自动调用的成员函数。构构造造函函数数的的名名称称与与类类名名相相同同,析析构构函函数数的的名名称称必必须须在在类类名名前前加加上上“”符符号号。注注意意,构构造造函函数数和和析析构构函函数数不不能能指指定定任任何何返返回回值值类类型型,包包括括voidvoid返回类型。返回类型。3.3 类的继承类的继承 n n继承继承继承继承是面向对象程序设计方法的四个基本特征之是面向对象程序设计方法的四个基本特征之一,是程序代码可重用性的具体体现。一,是程序代码可重用性的具体体现。n n在在C+C+面向对象程序设计中,所谓类的继承就是面向对象程序设计中,所谓类的继承就是利用现有的类创建一个新的类。新类继承了现有利用现有的类创建一个新的类。新类继承了现有类的属性和行为。类的属性和行为。n n为了使新类具有自己所需的功能,它可以扩充和为了使新类具有自己所需的功能,它可以扩充和完善现有类的属性和行为,使之更具体。完善现有类的属性和行为,使之更具体。n n微软基础类微软基础类MFCMFC就是通过类的继承来体现类的可就是通过类的继承来体现类的可重用性和可扩充性。重用性和可扩充性。3.3.1 基类和派生类基类和派生类 1.1.问题的提出问题的提出问题的提出问题的提出 n n在现实世界中,一类事物的对象常常也属于另在现实世界中,一类事物的对象常常也属于另一类事物。一类事物。n n在面向对象程序设计方法中,一个类的对象也在面向对象程序设计方法中,一个类的对象也常常是另一个类的对象,即一个类具有了另一常常是另一个类的对象,即一个类具有了另一个类的属性和方法。个类的属性和方法。n n在定义一个类时,根据类的继承性,我们能够在定义一个类时,根据类的继承性,我们能够且应尽可能地利用现有的类来定制新的类,而且应尽可能地利用现有的类来定制新的类,而不必重新设计新的类。不必重新设计新的类。n n2.基类和派生类的概念基类和派生类的概念在继承关系中,新定义的类称为被继承类的在继承关系中,新定义的类称为被继承类的派生派生派生派生类类类类或或子类子类子类子类,而被继承的类称为新定义类的,而被继承的类称为新定义类的基类基类基类基类或或父类父类父类父类。派生类继承了基类的所有成员。派生类继承了基类的所有成员。一个派生类也可以作为另一个派生类的基类。一个派生类也可以作为另一个派生类的基类。3.派生类的定义派生类的定义 class class :./派生类新增加的成员声明列表派生类新增加的成员声明列表;说明:说明:说明:说明:n n派生方式决定了基类的成员在派生类中的访问权限。派生方式共有三种:public、private和protected(缺省值为private)。n n虽然派生类继承了基类的所有成员,但为了不破坏基类的封装性,无论采用哪种派生方式,基类的私有成员在派生类中都是不可见的,即不允许在派生类的成员函数中访问基类的私有成员。三种派生方式的区别:三种派生方式的区别:采用采用publicpublic派生,基类成员的访问权限在派生类中派生,基类成员的访问权限在派生类中保持不变,即基类所有的公有或保护成员在派生保持不变,即基类所有的公有或保护成员在派生类中仍为公有或保护成员。类中仍为公有或保护成员。publicpublic派生最常用。派生最常用。(1)(1)可以在派生类的成员函数中访问基类的非可以在派生类的成员函数中访问基类的非私有成员;私有成员;(2)(2)可通过派生类的对象直接访问基类的公有可通过派生类的对象直接访问基类的公有成员。成员。采用采用privateprivate私有派生,基类所有的公有和保护成员私有派生,基类所有的公有和保护成员在派生类中都成为私有成员,只允许在派生类的在派生类中都成为私有成员,只允许在派生类的成员函数中访问基类的非私有成员。成员函数中访问基类的非私有成员。privateprivate派生派生很少使用。很少使用。采用采用protectedprotected保护派生,基类所有的公有和保护保护派生,基类所有的公有和保护成员在派生类中都成为保护成员,只允许在派生成员在派生类中都成为保护成员,只允许在派生类的成员函数和该派生类的派生类的成员函数中类的成员函数和该派生类的派生类的成员函数中访问基类的非私有成员。访问基类的非私有成员。例例 定义类定义类PointPoint,然后定义类,然后定义类PointPoint的派生类的派生类CircleCircle。#include#include class Pointclass Point/定义基类,表示点定义基类,表示点 private:private:intint x;x;intint y;y;public:public:void void setPoint(intsetPoint(int a,a,intint b)x=a;y=b;/b)x=a;y=b;/设置设置坐标坐标 intint getXgetX()return x;()return x;/取得取得X X坐标坐标 intint getYgetY()return y;()return y;/取得取得Y Y坐标坐标;class Circle:class Circle:publicpublic Point /Point /定义派生类,表示圆定义派生类,表示圆 private:private:intint radius;radius;public:public:void void setRadius(intsetRadius(int r)radius=r;/r)radius=r;/设置半径设置半径 intint getRadiusgetRadius()return radius;/()return radius;/取得半径取得半径 intint getUpperLeftXgetUpperLeftX()return ()return getXgetX()()radius;radius;/取得外接正方形左上角的取得外接正方形左上角的X X坐标坐标 intint getUpperLeftYgetUpperLeftY()return ()return getYgetY()+radius;()+radius;/取得外接正方形左上角的取得外接正方形左上角的Y Y坐标坐标;main()main()Circle c;Circle c;c.setPoint(200,250);c.setPoint(200,250);c.setRadius(100);c.setRadius(100);coutcoutX=X=c.getXc.getX(),Y=(),Y=c.getYc.getY()(),Radius=,Radius=c.getRadiusc.getRadius()()endlendl;coutcoutUpperLeftUpperLeft X=X=c.getUpperLeftXc.getUpperLeftX()(),UpperLeftUpperLeft Y=Y=c.getUpperLeftYc.getUpperLeftY()()endlendl;程序运行结果:程序运行结果:X=200X=200,Y=250Y=250,Radius=100Radius=100UpperLeftUpperLeft X=100 X=100,UpperLeftUpperLeft Y=350 Y=350 说明:说明:n n派生类派生类CircleCircle通过通过publicpublic派生方式继承了基类派生方式继承了基类PointPoint的所有成员的所有成员(除私有成员外所有成员的访问权限不除私有成员外所有成员的访问权限不变变),同时还定义了自己的成员变量和成员函数。,同时还定义了自己的成员变量和成员函数。n n若将类若将类CircleCircle的派生方式改为的派生方式改为privateprivate或或protectedprotected,则下述语句是非法的:则下述语句是非法的:c.setPoint(200,250);c.setPoint(200,250);l l无无论论哪哪种种派派生生方方式式,派派生生类类都都继继承承了了基基类类的的所所有有成成员员,包包括括私私有有成成员员。我我们们虽虽然然不不能能在在派派生生类类CircleCircle中中直直接接访访问问私私有有数数据据成成员员x x和和y y,但但可可以以通通过过继继承承的的公公有有成成员员函函数数getXgetX()()、getYgetY()()和和setPointsetPoint()()访问或设置它们。访问或设置它们。最后一个问题:最后一个问题:n n利用类继承定义类可能带来一个利用类继承定义类可能带来一个问题问题问题问题:派生类会:派生类会继承它不需要的基类中的数据成员和成员函数,继承它不需要的基类中的数据成员和成员函数,这时,基类中不适合于派生类的成员可以在派生这时,基类中不适合于派生类的成员可以在派生类中重新加以定义。类中重新加以定义。n n例例例例 派生类成员函数对基类成员函数的覆盖。派生类成员函数对基类成员函数的覆盖。#include#include class A class A public:public:void Show()void Show()coutcoutA:ShownA:Shown;class B:public Aclass B:public A public:public:void Show()void Show()coutcoutB:ShownB:Shown;/在派生类中重新定义成员函数在派生类中重新定义成员函数 void Display()Show();void Display()Show();/调用派生类调用派生类B B的成员函数的成员函数Show()Show();void main()void main()A a;A a;B b;B b;a.Showa.Show();();/调用基类调用基类A A的成员函数的成员函数Show()Show()b.Showb.Show();();/调用派生类调用派生类B B的成员函数的成员函数Show()Show()b.Displayb.Display();();程序运行结果:程序运行结果:A:ShowB:ShowB:Shown n从本例可以看出,虽然派生类继承了基类的所有成员函数,但如果派生类某个成员函数的名称和参数与基类成员函数一致(即在派生类中对该成员函数重新进行了定义),则在派生类中调用的成员函数是派生类的成员函数。n n 请问:如果在派生类B中没有对成员函数Show()重新进行定义(重载),程序运行结果如何?