C++语言程序设计第3版清华大学教程.ppt
第四章第四章 类与对象类与对象C+语言程序设计清华大学清华大学 郑郑 莉莉1C+语言程序设计清华大学 郑莉本章主要内容本章主要内容l面向对象的思想面向对象的思想lOOP的基本特点的基本特点l类概念和声明类概念和声明l对象对象l构造函数构造函数l析构函数析构函数l内联成员函数内联成员函数l拷贝构造函数拷贝构造函数l类的组合类的组合2C+语言程序设计清华大学 郑莉回顾:面向过程的设计方法回顾:面向过程的设计方法l重点重点:如何实现的细节和过程,将数据与函数分开。l形式:形式:主模块+若干个子模块(main()+子函数)。l特点:特点:自顶向下,逐步求精功能分解。l缺点:缺点:效率低,程序的可重用性差。面向对象的思想3C+语言程序设计清华大学 郑莉面向对象的方法面向对象的方法l目的:目的:实现软件设计的产业化。l观点:观点:自然界是由实体(对象)所组成。l程序设计方法:程序设计方法:使用面向对象的观点来描述模仿并处理现实问题。l要求:要求:高度概括、分类、和抽象。面向对象的思想4C+语言程序设计清华大学 郑莉抽象抽象抽象是对具体对象(问题)进行概括,抽象是对具体对象(问题)进行概括,抽出这一类对象的公共性质并加以描述抽出这一类对象的公共性质并加以描述的过程。的过程。先注意问题的本质及描述,其次是实现过程或细节。数据抽象:描述某类对象的属性或状态(对象相互区别的物理量)。代码抽象:描述某类对象的共有的行为特征或具有的功能。抽象的实现:通过类的声明。OOP的基本特点5C+语言程序设计清华大学 郑莉抽象实例抽象实例钟表钟表l数据抽象:数据抽象:int Hour,int Minute,int Secondl代码抽象:代码抽象:SetTime(),ShowTime()OOP的基本特点6C+语言程序设计清华大学 郑莉抽象实例抽象实例钟表类钟表类class Clock public:void SetTime(int NewH,int NewM,int NewS);void ShowTime();private:int Hour,Minute,Second;OOP的基本特点7C+语言程序设计清华大学 郑莉抽象实例抽象实例人人l数据抽象:数据抽象:char*name,char*gender,int age,int idl代码抽象:代码抽象:生物属性角度:GetCloth(),Eat(),Step(),社会属性角度:Work(),Promote(),OOP的基本特点8C+语言程序设计清华大学 郑莉封装封装将抽象出的数据成员、代码成员相结将抽象出的数据成员、代码成员相结合,将它们视为一个整体。合,将它们视为一个整体。目的是曾强安全性和简化编程,使用者不必了解具体的实现细节,而只需要通过外部接口,以特定的访问权限,来使用类的成员。实现封装:类声明中的OOP的基本特点9C+语言程序设计清华大学 郑莉封装封装l实例:实例:class Clock public:void SetTime(int NewH,int NewM,int NewS);void ShowTime();private:int Hour,Minute,Second;边界特定的访问权限OOP的基本特点外部接口10C+语言程序设计清华大学 郑莉继承与派生继承与派生是是C+中支持层次分类的一种机制,中支持层次分类的一种机制,允许程序员在保持原有类特性的基础上,允许程序员在保持原有类特性的基础上,进行更具体的说明。进行更具体的说明。实现:声明派生类实现:声明派生类见见第第7章章OOP的基本特点11C+语言程序设计清华大学 郑莉多态性多态性l多态:同一名称,不同的功能实现方式。多态:同一名称,不同的功能实现方式。l目的:达到行为标识统一,减少程序中标目的:达到行为标识统一,减少程序中标识符的个数。识符的个数。l实现:重载函数和虚函数实现:重载函数和虚函数见见第第8章章OOP的基本特点12C+语言程序设计清华大学 郑莉c+中的类中的类l类是具有相同属性和行为的一组对象类是具有相同属性和行为的一组对象的集合,它为属于该类的全部对象提的集合,它为属于该类的全部对象提供了统一的抽象描述,其内部包括属供了统一的抽象描述,其内部包括属性和行为两个主要部分。性和行为两个主要部分。l利用类可以实现数据的封装、隐藏、利用类可以实现数据的封装、隐藏、继承与派生。继承与派生。l利用类易于编写大型复杂程序,其模利用类易于编写大型复杂程序,其模块化程度比块化程度比C中采用函数更高。中采用函数更高。类 和 对 象13C+语言程序设计清华大学 郑莉类的声明形式类的声明形式 类是一种用户自定义类型,声明形式:类是一种用户自定义类型,声明形式:class 类名称 public:公有成员(外部接口)private:私有成员 protected:保护型成员类 和 对 象14C+语言程序设计清华大学 郑莉公有类型成员公有类型成员在关键字在关键字public后面声明,它们是类后面声明,它们是类与外部的接口,任何外部函数都可以访与外部的接口,任何外部函数都可以访问公有类型数据和函数。问公有类型数据和函数。类 和 对 象15C+语言程序设计清华大学 郑莉私有类型成员私有类型成员在关键字在关键字private后面声明,只允许后面声明,只允许本类中的函数访问,而类外部的任何函本类中的函数访问,而类外部的任何函数都不能访问。数都不能访问。如果紧跟在类名称的后面声明私有成员,如果紧跟在类名称的后面声明私有成员,则关键字则关键字privateprivate可以省略。可以省略。类 和 对 象16C+语言程序设计清华大学 郑莉保护类型保护类型与与private类似,其差别表现在继承与类似,其差别表现在继承与派生时对派生类的影响不同,第七章讲。派生时对派生类的影响不同,第七章讲。类 和 对 象17C+语言程序设计清华大学 郑莉类的成员类的成员class Clock public:void SetTime(int NewH,int NewM,int NewS);void ShowTime();private:int Hour,Minute,Second;类 和 对 象成员数据成员函数18void Clock:SetTime(int NewH,int NewM,void Clock:SetTime(int NewH,int NewM,int NewS)int NewS)Hour=NewH;Hour=NewH;Minute=NewM;Minute=NewM;Second=NewS;Second=NewS;void Clock:ShowTime()void Clock:ShowTime()coutHour:Minute:Second;coutHour:Minute:Second;1919C+语言程序设计清华大学 郑莉成员数据成员数据l与一般的变量声明相同,但需要将它与一般的变量声明相同,但需要将它放在类的声明体中。放在类的声明体中。类 和 对 象20C+语言程序设计清华大学 郑莉成员函数成员函数l在类中说明原型,可以在类外给出函数在类中说明原型,可以在类外给出函数体实现,并在函数名前使用类名加以限体实现,并在函数名前使用类名加以限定。也可以直接在类中给出函数体,形定。也可以直接在类中给出函数体,形成内联成员函数。成内联成员函数。l允许声明重载函数和带默认形参值的函允许声明重载函数和带默认形参值的函数数类 和 对 象21C+语言程序设计清华大学 郑莉内联成员函数内联成员函数l为了提高运行时的效率,对于较简单为了提高运行时的效率,对于较简单的函数可以声明为内联形式。的函数可以声明为内联形式。l内联函数体中不要有复杂结构(如循内联函数体中不要有复杂结构(如循环语句和环语句和switch语句)。语句)。l在类中声明内联成员函数的方式:在类中声明内联成员函数的方式:将函数体放在类的声明中。使用inline关键字。类 和 对 象22C+语言程序设计清华大学 郑莉内联成员函数举例内联成员函数举例(一一)class Pointclass Point public:public:void Init(int initX,int initY)void Init(int initX,int initY)X=initX;X=initX;Y=initY;Y=initY;int GetX()int GetX()return X;return X;int GetY()int GetY()return Y;return Y;private:private:int X,Y;int X,Y;类 和 对 象23C+语言程序设计清华大学 郑莉内联成员函数举例内联成员函数举例(二二)class Pointclass Point public:public:void Init(int initX,int initY);void Init(int initX,int initY);int GetX()int GetX();int GetY()int GetY();private:private:int X,Y;int X,Y;类 和 对 象24inlineinline void Point:void Point:Init(int initX,int initY)Init(int initX,int initY)X=initX;X=initX;Y=initY;Y=initY;inlineinline int Point:GetX()int Point:GetX()return X;return X;inlineinline int Point:GetY()int Point:GetY()return Y;return Y;2525C+语言程序设计清华大学 郑莉对象对象l类的对象是该类的某一特定实体,即类的对象是该类的某一特定实体,即类类型的变量。类类型的变量。l声明形式:声明形式:类名类名 对象名;对象名;l例:例:Clock myClock;Clock myClock;类 和 对 象26C+语言程序设计清华大学 郑莉类中成员的访问方式类中成员的访问方式l类中成员互访类中成员互访直接使用成员名l类外访问类外访问使用“对象名.成员名”方式访问 public 属性的成员类 和 对 象27C+语言程序设计清华大学 郑莉例例4-1类的应用举例类的应用举例#include#includeusing namespace std;using namespace std;class Clockclass Clock ././类的声明略类的声明略/./.类的实现略类的实现略int main()int main()Clock Clock myClockmyClock;myClock.SetTimemyClock.SetTime(8,30,30);(8,30,30);myClock.ShowTimemyClock.ShowTime();();类 和 对 象28C+语言程序设计清华大学 郑莉构造函数构造函数l构造函数的作用是在对象被创建时使构造函数的作用是在对象被创建时使用特定的值构造对象,或者说将对象用特定的值构造对象,或者说将对象初始化初始化为一个特定的状态。为一个特定的状态。l在对象创建时在对象创建时由系统自动调用由系统自动调用。l如果程序中未声明,则系统自动产生如果程序中未声明,则系统自动产生出一个出一个默认形式默认形式的构造函数的构造函数l允许为允许为内联内联函数、函数、重载重载函数、函数、带默认带默认形参值形参值的函数的函数构造函数和析构函数29C+语言程序设计清华大学 郑莉构造函数举例构造函数举例class Clockclass Clock public:public:Clock(int NewH,int NewM,int NewS);/Clock(int NewH,int NewM,int NewS);/构造函数构造函数void SetTime(int NewH,int NewM,int NewS);void SetTime(int NewH,int NewM,int NewS);void ShowTime();void ShowTime();private:private:int Hour,Minute,Second;int Hour,Minute,Second;构造函数和析构函数30构造函数的实现:构造函数的实现:Clock:Clock(int NewH,int NewM,int NewS)Clock:Clock(int NewH,int NewM,int NewS)Hour=NewH;Hour=NewH;Minute=NewM;Minute=NewM;Second=NewS;Second=NewS;建立对象时构造函数的作用:建立对象时构造函数的作用:int main()int main()Clock c(0,0,0);Clock c(0,0,0);/隐含调用构造函数,将初始值作为实参。隐含调用构造函数,将初始值作为实参。c.ShowTime();c.ShowTime();3131C+语言程序设计清华大学 郑莉拷贝构造函数拷贝构造函数拷贝构造函数是一种特殊的构造函数,其形参为本拷贝构造函数是一种特殊的构造函数,其形参为本类的对象引用。类的对象引用。class 类名 public:类名(形参);/构造函数 类名(类名&对象名);/拷贝构造函数 .;类名:类(类名&对象名)/拷贝构造函数的实现 函数体 构造函数和析构函数32C+语言程序设计清华大学 郑莉拷贝构造函数拷贝构造函数(例例4-2)class Point public:Point(int xx=0,int yy=0)X=xx;Y=yy;Point(Point&p);int GetX()return X;int GetY()return Y;private:int X,Y;构造函数和析构函数33Point:Point(Point&p)Point:Point(Point&p)X=p.X;X=p.X;Y=p.Y;Y=p.Y;cout cout拷贝构造函数被调用拷贝构造函数被调用endl;endl;3434C+语言程序设计清华大学 郑莉拷贝构造函数拷贝构造函数(例例4-2)l当用类的一个对象去初始化该类的另一个当用类的一个对象去初始化该类的另一个对象时系统自动调用拷贝构造函数实现拷对象时系统自动调用拷贝构造函数实现拷贝赋值。贝赋值。int main()int main()Point A(1,2);Point A(1,2);Point B(A);/Point B(A);/拷贝构造函数被调用拷贝构造函数被调用 coutB.GetX()endl;coutB.GetX()endl;构造函数和析构函数35C+语言程序设计清华大学 郑莉拷贝构造函数拷贝构造函数(例例4-2)l若函数的形参为类对象,调用函数时,若函数的形参为类对象,调用函数时,实参赋值给形参,系统自动调用拷贝实参赋值给形参,系统自动调用拷贝构造函数。例如:构造函数。例如:void fun1(Point p)void fun1(Point p)coutp.GetX()endl;coutp.GetX()endl;int main()int main()Point A(1,2);Point A(1,2);fun1(A);/fun1(A);/调用拷贝构造函数调用拷贝构造函数 构造函数和析构函数36C+语言程序设计清华大学 郑莉拷贝构造函数拷贝构造函数(例例4-2)l当函数的返回值是类对象时,系统自动调用当函数的返回值是类对象时,系统自动调用拷贝构造函数。例如:拷贝构造函数。例如:Point fun2()Point fun2()Point A(1,2);Point A(1,2);return A;/return A;/调用拷贝构造函数调用拷贝构造函数 int main()int main()Point B;Point B;B=fun2();B=fun2();构造函数和析构函数37C+语言程序设计清华大学 郑莉默认的拷贝构造函数默认的拷贝构造函数如果程序员没有为类声明拷贝初始如果程序员没有为类声明拷贝初始化构造函数,则编译器自己生成一个默化构造函数,则编译器自己生成一个默认的拷贝构造函数。认的拷贝构造函数。这个构造函数执行的功能是:用作这个构造函数执行的功能是:用作为初始值的对象的每个数据成员的值,为初始值的对象的每个数据成员的值,初始化将要建立的对象的对应数据成员。初始化将要建立的对象的对应数据成员。构造函数和析构函数38C+语言程序设计清华大学 郑莉析构函数析构函数l完成对象被删除前的一些清理工作。完成对象被删除前的一些清理工作。l在对象的生存期结束的时刻系统自动在对象的生存期结束的时刻系统自动调用它,然后再释放此对象所属的空调用它,然后再释放此对象所属的空间。间。l如果程序中未声明析构函数,编译器如果程序中未声明析构函数,编译器将自动产生一个默认的析构函数。将自动产生一个默认的析构函数。构造函数和析构函数39C+语言程序设计清华大学 郑莉构造函数和析构函数举例构造函数和析构函数举例#include#includeusing namespace std;using namespace std;class Pointclass Point public:public:Point(int xx,int yy);Point(int xx,int yy);Point();Point();/./.其他函数原型其他函数原型 private:private:int X,int Y;int X,int Y;构造函数和析构函数40Point:Point(int xx,int yy)Point:Point(int xx,int yy)X=xx;Y=yy;X=xx;Y=yy;Point:Point()Point:Point()/./.其他函数的实现略其他函数的实现略4141C+语言程序设计清华大学 郑莉类的应用举例类的应用举例(例例4-3)一圆形游泳池如图所示,现在需在其周围建一圆形游泳池如图所示,现在需在其周围建一圆形过道,并在其四周围上栅栏。栅栏价格为一圆形过道,并在其四周围上栅栏。栅栏价格为35元元/米,过道造价为米,过道造价为20元元/平方米。过道宽度为平方米。过道宽度为3米,游泳池半径由键盘输入。要求编程计算并米,游泳池半径由键盘输入。要求编程计算并输出过道和栅栏的造价。输出过道和栅栏的造价。游泳池过道42#include#include using namespace std;using namespace std;const float PI=3.14159;const float PI=3.14159;const float FencePrice=35;const float FencePrice=35;const float ConcretePrice=20;const float ConcretePrice=20;/声明类声明类Circle Circle 及其数据和方法及其数据和方法class Circleclass Circle private:private:float radius;float radius;public:public:Circle(float r);/Circle(float r);/构造函数构造函数 float Circumference()const;/float Circumference()const;/圆周长圆周长 float Area()const;/float Area()const;/圆面积圆面积;4343/类的实现类的实现/构造函数初始化数据成员构造函数初始化数据成员radiusradiusCircle:Circle(float r)Circle:Circle(float r)radius=rradius=r/计算圆的周长计算圆的周长float Circle:Circumference()constfloat Circle:Circumference()const return 2*PI*radius;return 2*PI*radius;/计算圆的面积计算圆的面积 float Circle:Area()constfloat Circle:Area()const return PI*radius*radius;return PI*radius*radius;4444void main()void main()float radius;float radius;float FenceCost,ConcreteCost;float FenceCost,ConcreteCost;/提示用户输入半径提示用户输入半径 coutEnter the radius of the pool:;coutradius;cinradius;/声明声明 Circle Circle 对象对象 Circle Pool(radius);Circle Pool(radius);Circle PoolRim(radius+3);Circle PoolRim(radius+3);4545 /计算栅栏造价并输出计算栅栏造价并输出 FenceCost=PoolRim.Circumference()*FencePrice;FenceCost=PoolRim.Circumference()*FencePrice;coutFencing Cost is coutFencing Cost is¥FenceCostendl;FenceCostendl;/计算过道造价并输出计算过道造价并输出 ConcreteCost=(PoolRim.Area()-ConcreteCost=(PoolRim.Area()-Pool.Area()*ConcretePrice;Pool.Area()*ConcretePrice;coutConcrete Cost is coutConcrete Cost is¥ConcreteCostendl;ConcreteCostendl;运行结果运行结果Enter the radius of the pool:10Enter the radius of the pool:10Fencing Cost is Fencing Cost is¥2858.852858.85Concrete Cost is Concrete Cost is¥4335.394335.394646C+语言程序设计清华大学 郑莉组合的概念组合的概念l类中的成员数据是另一个类的对象。类中的成员数据是另一个类的对象。l可以在已有抽象的基础上实现更复杂可以在已有抽象的基础上实现更复杂的抽象。的抽象。类 的 组 合47C+语言程序设计清华大学 郑莉举例举例class Point private:float x,y;/点的坐标 public:Point(float h,float v);/构造函数 float GetX(void);/取X坐标 float GetY(void);/取Y坐标 void Draw(void);/在(x,y)处画点;/.函数的实现略类 的 组 合48class Lineclass Line private:private:Point p1,p2;/Point p1,p2;/线段的两个端点线段的两个端点 public:public:Line(Point a,Point b);/Line(Point a,Point b);/构造函数构造函数 Void Draw(void);/Void Draw(void);/画出线段画出线段;/./.函数的实现略函数的实现略4949C+语言程序设计清华大学 郑莉类组合的构造函数设计类组合的构造函数设计l原则:不仅要负责对本类中的基本类型成原则:不仅要负责对本类中的基本类型成员数据赋初值,也要对对象成员初始化。员数据赋初值,也要对对象成员初始化。l声明形式:声明形式:类名:类名(对象成员所需的形参,本类成员形参):对象1(参数),对象2(参数),.本类初始化 类 的 组 合50C+语言程序设计清华大学 郑莉类组合的构造函数调用类组合的构造函数调用l构造函数调用顺序:先调用内嵌对象构造函数调用顺序:先调用内嵌对象的构造函数(按内嵌时的声明顺序,的构造函数(按内嵌时的声明顺序,先声明者先构造)。然后调用本类的先声明者先构造)。然后调用本类的构造函数。(析构函数的调用顺序相构造函数。(析构函数的调用顺序相反)反)l若调用默认构造函数(即无形参的),若调用默认构造函数(即无形参的),则内嵌对象的初始化也将调用相应的则内嵌对象的初始化也将调用相应的默认构造函数。默认构造函数。类 的 组 合51C+语言程序设计清华大学 郑莉类的组合举例(二)类的组合举例(二)class Part /class Part /部件类部件类 public:public:Part();Part();Part(int i);Part(int i);Part();Part();void Print();void Print();private:private:int val;int val;类 的 组 合52class Whole class Whole public:public:Whole();Whole();Whole(int i,int j,int k);Whole(int i,int j,int k);Whole();Whole();void Print();void Print();private:private:Part one;Part one;Part two;Part two;int date;int date;5353Whole:Whole()Whole:Whole()date=0;date=0;Whole:Whole(int i,int j,int k):Whole:Whole(int i,int j,int k):two(i),one(j),date(k)two(i),one(j),date(k)/./.其他函数的实现略其他函数的实现略5454C+语言程序设计清华大学 郑莉互相引用的两个类互相引用的两个类class Aclass A public:public:void f(B b);void f(B b);class Bclass B public:public:void g(A a);void g(A a);编译错误:编译错误:ref.cpp(3):error C2061:syntax error:identifier B类 的 组 合55C+语言程序设计清华大学 郑莉前向引用声明前向引用声明class B;/class B;/前向引用声明前向引用声明class Aclass A public:public:void f(B b);void f(B b);class Bclass B public:public:void g(A a);void g(A a);类 的 组 合56C+语言程序设计清华大学 郑莉前向引用声明前向引用声明l类应该先声明,后使用类应该先声明,后使用l如果需要在某个类的声明之前,引用如果需要在某个类的声明之前,引用该类,则应进行前向引用声明。该类,则应进行前向引用声明。l前向引用声明只为程序引入一个标识前向引用声明只为程序引入一个标识符,但具体声明在其他地方。符,但具体声明在其他地方。类 的 组 合57C+语言程序设计清华大学 郑莉前向引用声明注意事项前向引用声明注意事项l使用前向引用声明虽然可以解决一些问题,但它并使用前向引用声明虽然可以解决一些问题,但它并不是万能的。需要注意的是,尽管使用了前向引用不是万能的。需要注意的是,尽管使用了前向引用声明,但是在提供一个完整的类声明之前,不能声声明,但是在提供一个完整的类声明之前,不能声明该类的对象,也不能在内联成员函数中使用该类明该类的对象,也不能在内联成员函数中使用该类的对象。请看下面的程序段:的对象。请看下面的程序段:class Fred;/前向引用声明class Barney Fred x;/错误:类Fred的声明尚不完善;class Fred Barney y;类 的 组 合58C+语言程序设计清华大学 郑莉前向引用声明注意事项前向引用声明注意事项l应该记住:当你使用前向引用声明时,应该记住:当你使用前向引用声明时,你只能使用被声明的符号,而不能涉你只能使用被声明的符号,而不能涉及类的任何细节。及类的任何细节。类 的 组 合59C+语言程序设计清华大学 郑莉小结与复习建议小结与复习建议l主要内容主要内容面向对象的基本概念、类和对象的声明、构造函数、析构函数、内联成员函数、拷贝构造函数、类的组合l达到的目标达到的目标学会类与对象的声明和使用。l实验任务实验任务实验四71