1、目录面向对象程序设计的基本特点类和对象构造函数和析构函数内联成员函数类的组合前向引用声明枚举类抽象、封装、继承、多态抽象实例钟表class Clock public:void setTime(int newH,int newM,int newS);void showTime();private:int hour,minute,second;抽象对同一类对象的共同属性和行为进行概括,形成类。先注意问题的本质,其次是实现过程或细节。数据抽象:描述某类对象的属性或状态(对象相互区别的物理量)。代码抽象:描述某类对象的共有的行为特征或具有的功能。抽象的实现:类。类的封装举例class Clock pu
2、blic:void setTime(int newH,int newM,int newS);void showTime();private:int hour,minute,second;边界特定的访问权限外部接口封装将抽象出的数据、代码封装在一起,形成类。目的:增强安全性和简化编程,使用者不必了解具体的实现细节,而只需要通过外部接口,以特定的访问权限,来使用类的成员。实现封装:类声明中的继承在已有类的基础上,进行扩展形成新的类。详见第7章多态 多态:同一名称,不同的功能实现方式。目的:达到行为标识统一,减少程序中标识符的个数。实现:重载函数和虚函数见第8章 对象是现实中的对象在程序中的模拟。类
3、是同一类对象的抽象,对象是类的某一特定实体。定义类的对象,才可以通过对象使用类中定义的功能。例4_1-1:钟表类类的定义#includeusing namespace std;class Clockpublic:void setTime(int newH=0,int newM=0,int newS=0);void showTime();private:int hour,minute,second;;例4_1-1:钟表类成员函数的实现void Clock:setTime(int newH,int newM,int newS)hour=newH;minute=newM;second=newS;vo
4、id Clock:showTime()cout hour :minute :second endl;例4_1-1:钟表类对象的使用int main()Clock myClock;myClock.setTime(8,30,30);myClock.showTime();return 0;运行结果:8:30:30设计类就是设计类型 此类型的“合法值”是什么?此类型应该有什么样的函数和操作符?新类型的对象该如何被创建和销毁?如何进行对象的初始化和赋值?对象作为函数的参数如何以值传递?谁将使用此类型的对象成员?类定义的语法形式class 类名称 public:公有成员(外部接口)private:私有成员
5、 protected:保护型成员类内初始值 可以为数据成员提供一个类内初始值 在创建对象时,类内初始值用于初始化数据成员 没有初始值的成员将被默认初始化。类内初始值举例class Clock public:void setTime(int newH,int newM,int newS);void showTime();private:int hour=0,minute=0,second=0;类内初始值类成员的访问控制 公有类型成员 私有类型成员 保护类型成员类成员的访问控制 公有类型成员 在关键字public后面声明,它们是类与外部的接口,任何外部函数都可以访问公有类型数据和函数。类成员的访问
6、控制 私有类型成员 在关键字private后面声明,只允许本类中的函数访问,而类外部的任何函数都不能访问。如果紧跟在类名称的后面声明私有成员,则关键字private可以省略。类成员的访问控制 保护类型成员 与private类似,其差别表现在继承与派生时对派生类的影响不同,详见第七章。对象定义的语法类名 对象名;例:Clock myClock;类中成员互相访问直接使用成员名访问类外访问使用“对象名.成员名”方式访问 public 属性的成员类的成员函数 在类中说明函数原型;可以在类外给出函数体实现,并在函数名前使用类名加以限定;也可以直接在类中给出函数体,形成内联成员函数;允许声明重载函数和带默
7、认参数值的函数。内联成员函数 为了提高运行时的效率,对于较简单的函数可以声明为内联形式。内联函数体中不要有复杂结构(如循环语句和switch语句)。在类中声明内联成员函数的方式:将函数体放在类的声明中。使用inline关键字。定义对象时,如何进行初始化呢?对象的初始化方法需要在程序中规定好,为此,C+语法提供了一个特殊的机制构造函数,下面我们就具体来学习如何在构造函数中规定对象的初始化方法例4_1-2:构造函数/类定义class Clock public:Clock(int newH,int newM,int newS);/构造函数void setTime(int newH,int newM,
8、int newS);void showTime();private:int hour,minute,second;例4_1-2:构造函数/构造函数的实现:Clock:Clock(int newH,int newM,int newS):hour(newH),minute(newM),second(newS)/其它函数实现同例4_1int main()Clock c(0,0,0);/自动调用构造函数 c.showTime();return 0;例4_1-2:构造函数class Clock public:Clock(int newH,int newM,int newS);/构造函数Clock();/
9、默认构造函数void setTime(int newH,int newM,int newS);void showTime();private:int hour,minute,second;Clock:Clock():hour(0),minute(0),second(0)/默认构造函数/其它函数实现同前例4_1-2:构造函数int main()Clock c1(0,0,0);/调用有参数的构造函数 Clock c2;/调用无参数的构造函数 构造函数的作用 在对象被创建时使用特定的值构造对象,将对象初始化为一个特定的初始状态。例如:希望在构造一个Clock类对象时,将初试时间设为0:0:0,就可以
10、通过构造函数来设置。构造函数的形式 函数名与类名相同;不能定义返回值类型,也不能有return语句;可以有形式参数,也可以没有形式参数;可以是内联函数;可以重载;可以带默认参数值。构造函数的调用时机 在对象创建时被自动调用 例如:Clock myClock(0,0,0);默认构造函数 调用时可以不需要实参的构造函数 参数表为空的构造函数 全部参数都有默认值的构造函数 下面两个都是默认构造函数,如在类中同时出现,将产生编译错误:Clock();Clock(int newH=0,int newM=0,int newS=0);隐含生成的构造函数 如果程序中未定义构造函数,编译器将在需要时自动生成一个
11、默认构造函数 参数列表为空,不为数据成员设置初始值;如果类内定义了成员的初始值,则使用类内定义的初始值;如果没有定义类内的初始值,则以默认方式初始化;基本类型的数据默认初始化的值是不确定的。“=default”如果类中已定义构造函数,默认情况下编译器就不再隐含生成默认构造函数。如果此时依然希望编译器隐含生成默认构造函数,可以使用“=default”。例如class Clock public:Clock()=default;/指示编译器提供默认构造函数Clock(int newH,int newM,int newS);/构造函数private:int hour,minute,second;类中往
12、往有多个构造函数,只是参数表和初始化列表不同,其初始化算法都是相同的,这时,为了避免代码重复,可以使用委托构造函数。回顾Clock类的两个构造函数:Clock(int newH,int newM,int newS):hour(newH),minute(newM),second(newS)/构造函数Clock:Clock():hour(0),minute(0),second(0)/默认构造函数委托构造函数 委托构造函数使用类的其他构造函数执行初始化过程 例如:Clock(int newH,int newM,int newS):hour(newH),minute(newM),second(newS
13、)Clock():Clock(0,0,0)我们经常会需要用一个也已经存在的对象,去初始化新的对象,这时就需要一种特殊的构造函数复制构造函数;默认的复制构造函数可以实现对应数据成员一一复制;自定义的默认构造函数可以实现特殊的复制功能。例4-2:Point类class Point /Point 类的定义public:Point(int xx=0,int yy=0)x=xx;y=yy;/构造函数,内联Point(const Point&p);/复制构造函数 void setX(int xx)x=xx;void setY(int yy)y=yy;int getX()const return x;/常函
14、数(第5章)int getY()const return y;/常函数(第5章)private:int x,y;/私有数据;例4-2:Point类/复制构造函数的实现Point:Point(const Point&p)x=p.x;y=p.y;cout Calling the copy constructor endl;例4-2:Point类/形参为Point类对象void fun1(Point p)cout p.getX()endl;/返回值为Point类对象Point fun2()Point a(1,2);return a;int main()Point a(4,5);Point b(a);
15、/用a初始化b。cout b.getX()endl;fun1(b);/对象b作为fun1的实参b=fun2();/函数的返回值是类对象cout b.getX()endl;return 0;复制构造函数定义复制构造函数是一种特殊的构造函数,其形参为本类的对象引用。作用是用一个已存在的对象去初始化同类型的新对象。class 类名 public:类名(形参);/构造函数 类名(const 类名&对象名);/复制构造函数 /.;类名:类(const 类名&对象名)/复制构造函数的实现 函数体 隐含的复制构造函数 如果程序员没有为类声明拷贝初始化构造函数,则编译器自己生成一个隐含的复制构造函数。这个构造
16、函数执行的功能是:用作为初始值的对象的每个数据成员的值,初始化将要建立的对象的对应数据成员。“=delete”如果不希望对象被复制构造 C+98做法:将复制构造函数声明为private,并且不提供函数的实现。C+11做法:用“=delete”指示编译器不生成默认复制构造函数。例:class Point /Point 类的定义public:Point(int xx=0,int yy=0)x=xx;y=yy;/构造函数,内联Point(const Point&p)=delete;/指示编译器不生成默认复制构造函数private:int x,y;/私有数据;复制构造函数被调用的三种情况 定义一个对象
17、时,以本类另一个对象作为初始值,发生复制构造;如果函数的形参是类的对象,调用函数时,将使用实参对象初始化形参对象,发生复制构造;如果函数的返回值是类的对象,函数执行完成返回主调函数时,将使用return语句中的对象初始化一个临时无名对象,传递给主调函数,此时发生复制构造。这种情况如何避免不必要的复制?左值与右值 左值是位于赋值语句左侧的对象变量,右值是位于赋值语句右侧的值,右值不依附于对象。int i=42;/i 是左值,42是右值 int foolbar();int j=foolbar();/foolbar()是右值 左值和右值之间的转换 int i=5,j=6;/i 和 j 是左值 int
18、 k=i+j;/自动转化为右值表达式右值引用 对持久存在变量的引用称为左值引用,用&表示(即第3章引用类型)对短暂存在可被移动的右值的引用称之为右值引用,用&表示 float n=6;float&lr_n=n;/左值引用 float&rr_n=n;/错误,右值引用不能绑定到左值 float&rr_n=n*n;/右值表达式绑定到右值引用 通过标准库中的move函数可将左值对象移动为右值 float n=10;float&rr_n=std:move(n);/将n转化为右值 使用move函数承诺除对n重新赋值或销毁外,不以rr_n以外方式使用移动构造函数基于右值引用,移动构造函数通过移动数据方式构造
19、新对象,与复制构造函数类似,移动构造函数参数为该类对象的右值引用。示例如下#includeclass astring public:std:string s;astring(astring&o)noexcept:s(std:move(o.s)/显式移动所有成员 函数体 移动构造函数不分配新内存,理论上不会报错,为配合异常捕获机制,需声明noexcept表明不会抛出异常(将于12章异常处理介绍)被移动的对象不应再使用,需要销毁或重新赋值移动构造示意 如果临时对象在被复制后就不再使用了,就完全可以把临时对象的资源直接移动,这样就避免了多余的复制操作。什么时候该触发移动构造?有可被利用的临时对象,例
20、如函数返回局部对象时,为避免不必要的复制构造,可以使用移动构造。临时对象临时对象例:构造函数和析构函数#include using namespace std;class Point public:Point(int xx,int yy);Point();/.其他函数原型private:int x,y;Point:Point(int xx,int yy)x=xx;y=yy;Point:Point()/.其他函数的实现略析构函数 完成对象被删除前的一些清理工作。在对象的生存期结束的时刻系统自动调用它,然后再释放此对象所属的空间。如果程序中未声明析构函数,编译器将自动产生一个默认的析构函数,其函数
21、体为空。这一节我们学习类的组合。在制造业,我们一直在用部件组装的生产模式,在程序中是否也可以用这种方法呢?C+语言支持这种部件组装的思想,就是类组合的机制:在定义一个新类时,可以用已有类的对象作为成员。例4_4:类的组合,线段(Line)类#include#include using namespace std;class Point/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;例4_4:类的组合,线段(
22、Line)类Point:Point(Point&p)/复制构造函数的实现x=p.x;y=p.y;cout Calling the copy constructor of Point endl;例4_4:类的组合,线段(Line)类/类的组合class Line /Line类的定义public:/外部接口Line(Point xp1,Point xp2);Line(Line&l);double getLen()return len;private:/私有数据成员Point p1,p2;/Point类的对象p1,p2double len;例4_4:类的组合,线段(Line)类/组合类的构造函数Li
23、ne:Line(Point xp1,Point xp2):p1(xp1),p2(xp2)cout Calling constructor of Line endl;double x=static_cast(p1.getX()-p2.getX();double y=static_cast(p1.getY()-p2.getY();len=sqrt(x*x+y*y);Line:Line(Line&l):p1(l.p1),p2(l.p2)/组合类的复制构造函数cout Calling the copy constructor of Line endl;len=l.len;例4_4:类的组合,线段(Li
24、ne)类/主函数int main()Point myp1(1,1),myp2(4,5);/建立Point类的对象Line line(myp1,myp2);/建立Line类的对象Line line2(line);/利用复制构造函数建立一个新对象cout The length of the line is:;cout line.getLen()endl;cout The length of the line2 is:;cout line2.getLen()endl;return 0;运行结果如下:Calling the copy constructor of PointCalling the co
25、py constructor of PointCalling the copy constructor of PointCalling the copy constructor of PointCalling constructor of LineCalling the copy constructor of PointCalling the copy constructor of PointCalling the copy constructor of LineThe length of the line is:5The length of the line2 is:5组合的概念 类中的成员
26、是另一个类的对象。可以在已有抽象的基础上实现更复杂的抽象。类组合的构造函数设计 原则:不仅要负责对本类中的基本类型成员数据初始化,也要对对象成员初始化。声明形式:类名:类名(对象成员所需的形参,本类成员形参):对象1(参数),对象2(参数),./函数体其他语句构造组合类对象时的初始化次序 首先对构造函数初始化列表中列出的成员(包括基本类型成员和对象成员)进行初始化,初始化次序是成员在类体中定义的次序。成员对象构造函数调用顺序:按对象成员的声明顺序,先声明者先构造。初始化列表中未出现的成员对象,调用用默认构造函数(即无形参的)初始化 处理完初始化列表之后,再执行构造函数的函数体。例:class
27、B;/前向引用声明class A public:void f(B b);class B public:void g(A a);前向引用声明 类应该先声明,后使用 如果需要在某个类的声明之前,引用该类,则应进行前向引用声明。前向引用声明只为程序引入一个标识符,但具体声明在其他地方。例:class Fred;/前向引用声明class Barney Fred x;/错误:类Fred的声明尚不完善;class Fred Barney y;前向引用声明注意事项 使用前向引用声明虽然可以解决一些问题,但它并不是万能的。在提供一个完整的类声明之前,不能声明该类的对象,也不能在内联成员函数中使用该类的对象。当
28、使用前向引用声明时,只能使用被声明的符号,而不能涉及类的任何细节。结构体 结构体是一种特殊形态的类 与类的唯一区别:类的缺省访问权限是private,结构体的缺省访问权限是public 结构体存在的主要原因:与C语言保持兼容 什么时候用结构体而不用类 定义主要用来保存数据、而没有什么操作的类型 人们习惯将结构体的数据成员设为公有,因此这时用结构体更方便结构体的定义 结构体定义struct 结构体名称 公有成员protected:保护型成员private:私有成员;结构体的初始化 如果一个结构体的全部数据成员都是公共成员,并且没有用户定义的构造函数,没有基类和虚函数(基类和虚函数将在后面的章节中
29、介绍),这个结构体的变量可以用下面的语法形式赋初值 类型名 变量名=成员数据1初值,成员数据2初值,;例4-7用结构体表示学生的基本信息#include#include#include using namespace std;struct Student/学生信息结构体int num;/学号string name;/姓名,字符串对象,将在第6章详细介绍char sex;/性别int age;/年龄;例4-7(续)int main()Student stu=97001,Lin Lin,F,19;cout Num:stu.num endl;cout Name:stu.name endl;cout
30、Sex:stu.sex endl;cout Age:stu.age endl;return 0;运行结果:Num:97001Name:Lin LinSex:FAge:19联合体 声明形式union 联合体名称 公有成员protected:保护型成员private:私有成员;特点:成员共用同一组内存单元 任何两个成员不会同时有效联合体的内存分配union Mark/表示成绩的联合体char grade;/等级制的成绩bool pass;/只记是否通过课程的成绩int percent;/百分制的成绩;Markgradepercentpass无名联合例:union int i;float f;在程序
31、中可以这样使用:i=10;f=2.2;无名联合没有标记名,只是声明一个成员项的集合,这些成员项具有相同的内存地址,可以由成员项的名字直接访问。例4-8使用联合体保存成绩信息,并且输出。#include#include using namespace std;class ExamInfo private:string name;/课程名称enum GRADE,PASS,PERCENTAGE mode;/计分方式union char grade;/等级制的成绩bool pass;/只记是否通过课程的成绩int percent;/百分制的成绩;例4-8(续)public:/三种构造函数,分别用等级、
32、是否通过和百分初始化ExamInfo(string name,char grade):name(name),mode(GRADE),grade(grade)ExamInfo(string name,bool pass):name(name),mode(PASS),pass(pass)ExamInfo(string name,int percent):name(name),mode(PERCENTAGE),percent(percent)void show();例4-8(续)void ExamInfo:show()cout name :;switch(mode)case GRADE:cout g
33、rade;break;case PASS:cout (pass?PASS:FAIL);break;case PERCENTAGE:cout percent;break;cout endl;例4-8(续)int main()ExamInfo course1(English,B);ExamInfo course2(Calculus,true);ExamInfo course3(C+Programming,85);course1.show();course2.show();course3.show();return 0;运行结果:English:BCalculus:PASSC+Programming
34、:85枚举类型 只要将需要的变量值一一列举出来,便构成了一个枚举类型。C+包含两种枚举类型:不限定作用域的枚举类型和限定作用域的枚举类。不限定作用域枚举类型声明形式如下:enum 枚举类型名 变量值列表;例如:enum Weekday SUN,MON,TUE,WED,THU,FRI,SAT;关于限定作用域的enum类将在第4章和第5章详细介绍。不限定作用域枚举类型说明 对枚举元素按常量处理,不能对它们赋值。例如,不能写:SUN=0;枚举元素具有默认值,它们依次为:0,1,2,.。也可以在声明时另行指定枚举元素的值,如:enum WeekdaySUN=7,MON=1,TUE,WED,THU,FR
35、I,SAT;枚举值可以进行关系运算。整数值不能直接赋给枚举变量,如需要将整数赋值给枚举变量,应进行强制类型转换。例4-9 设某次体育比赛的结果有四种可能:胜(WIN)、负(LOSE)、平局(TIE)、比赛取消(CANCEL),编写程序顺序输出这四种情况。分析:由于比赛结果只有四种可能,所以可以声明一个枚举类型,声明一个枚举类型的变量来存放比赛结果。例4-9#include using namespace std;enum GameResult WIN,LOSE,TIE,CANCEL;int main()GameResult result;enum GameResult omit=CANCEL;
36、for(int count=WIN;count=CANCEL;count+)result=GameResult(count);if(result=omit)cout The game was cancelled endl;else cout The game was played;if(result=WIN)cout and we won!;if(result=LOSE)cout and we lost.;cout endl;return 0;运行结果 The game was played and we won!The game was played and we lost.The game
37、 was played The game was cancelled枚举类强类型枚举 定义语法形式enum class 枚举类型名:底层类型 枚举值列表;例:enum class Type General,Light,Medium,Heavy;enum class Type:char General,Light,Medium,Heavy;enum class Category General=1,Pistol,MachineGun,Cannon;枚举类的优势 强作用域,其作用域限制在枚举类中。例:使用Type的枚举值General:Type:General 转换限制,枚举类对象不可以与整型隐式
38、地互相转换。可以指定底层类型 例:enum class Type:char General,Light,Medium,Heavy;枚举类举例#includeusing namespace std;enum class Side Right,Left;enum class Thing Wrong,Right;/不冲突int main()Side s=Side:Right;Thing w=Thing:Wrong;cout (s=w)endl;/编译错误,无法直接比较不同枚举类 return 0;UML简介 UML(Unified Modeling Language)语言是一种可视化的的面向对象建模
39、语言。UML有三个基本的部分 事物(Things)UML中重要的组成部分,在模型中属于最静态的部分,代表概念上的或物理上的元素 关系(Relationships)关系把事物紧密联系在一起 图(Diagrams)图是很多有相互相关的事物的组UML类图 举例:Clock类的完整表示 Clock类的简洁表示Clock-hour:int-minute:int-second:int+showTime():void+setTime(newH:int=0,newM:int=0,newS:int=0):voidClock对象图myClock:Clock-hour:int-minute:int-second:i
40、ntmyClock:Clock几种关系的图形标识 依赖关系图中的“类A”是源,“类B”是目标,表示“类A”使用了“类B”,或称“类A”依赖“类B”类 A类 B几种关系的图形标识 作用关系关联图中的“重数A”决定了类B的每个对象与类A的多少个对象发生作用,同样“重数B”决定了类A的每个对象与类B的多少个对象发生作用。类 A重数A类 B重数B几种关系的图形标识 包含关系聚集和组合 共享聚集 组成聚集(组合)聚集表示类之间的关系是整体与部分的关系,“包含”、“组成”、“分为部分”等都是聚集关系。共享聚集:部分可以参加多个整体;组成聚集(组合):整体拥有各个部分,整体与部分共存,如果整体不存在了,那么
41、部分也就不存在了。类 A类 B重数A重数B类 A类 B重数A重数B例4-5 采用UML方法来描述例4-4中Line类和Point类的关系Point-x:int-y:int+Point(xx:int=0,yy:int=0)+Point(p:Point&)+getX():int+getY():intLine-len:double+Line(xp1:Point,xp2:Point)+Line(:Line&)+getLen():double-p1-p21.*21.*几种关系的图形标识父类 A父类 B子类 1子类 2继承关系泛化注释 在UML图形上,注释表示为带有褶角的矩形,然后用虚线连接到UML的其他
42、元素上,它是一种用于在图中附加文字注释的机制。1004.5 UML图形标识 4.5.2 UML类图注释文字例4-6 带有注释的Line类和Point类关系的描述Point-x:int-y:int+Point(xx:int=0,yy:int=0)+Point(p:Point&)+getX():int+getY():intLine-len:double+Line(xp1:Point,xp2:Point)+Line(:Line&)+getLen():double-p1-p21.*21.*单向组合:直线段包含端点p1、p2小结 主要内容 面向对象的基本概念、类和对象的声明、构造函数、析构函数、内联成员函数、复制构造函数、类的组合 达到的目标 掌握面向对象的基本概念;掌握类设计的思想、类和对象声明的语法;理解构造函数、复制构造函数和析构函数的作用和调用过程,掌握相关的语法;理解内联成员函数的作用,掌握相关语法;理解类的组合在面向对象设计中的意义,掌握类组合的语法。
侵权处理QQ:3464097650--上传资料QQ:3464097650
【声明】本站为“文档C2C交易模式”,即用户上传的文档直接卖给(下载)用户,本站只是网络空间服务平台,本站所有原创文档下载所得归上传人所有,如您发现上传作品侵犯了您的版权,请立刻联系我们并提供证据,我们将在3个工作日内予以改正。