1、 3.1 3.1 类类描述了一组具有相同属性和行为特征的对象。在系统实描述了一组具有相同属性和行为特征的对象。在系统实现中,类是一种现中,类是一种共享共享机制,它提供了本类对象共享的操作机制,它提供了本类对象共享的操作实现。类是代码复用的基本单位,它可以实现抽象数据类实现。类是代码复用的基本单位,它可以实现抽象数据类型,创建对象,实现属性和行为的封装型,创建对象,实现属性和行为的封装。v类是一种复杂的数据类型,它是将不同类型的数据和与这类是一种复杂的数据类型,它是将不同类型的数据和与这些数据相关的操作封装在一起的集合体。些数据相关的操作封装在一起的集合体。数据结数据结构构操作1操作2操作3操作
2、4操作5数据数据结构结构 3.1 3.1 类类v对象是类的实例对象是类的实例。类是对一组具有相同特征的对象的抽象类是对一组具有相同特征的对象的抽象描述,所有这些对象都是这个类的实例。描述,所有这些对象都是这个类的实例。v在程序设计语言中,在程序设计语言中,类是一种数据类型,而对象是该类型类是一种数据类型,而对象是该类型的变量的变量,变量名即是某个具体对象的标识。,变量名即是某个具体对象的标识。v类提供了完整的解决特定问题的能力,因为类描述了类提供了完整的解决特定问题的能力,因为类描述了数据数据结构结构(对象属性)、(对象属性)、算法算法(对象行为)和(对象行为)和外部接口外部接口(消息(消息协
3、议)。协议)。v在在C+C+语言中,一个类的定义包含语言中,一个类的定义包含数据成员和成员函数数据成员和成员函数两部两部分内容。数据成员定义该类对象的属性,不同对象的属性分内容。数据成员定义该类对象的属性,不同对象的属性值可以不同;成员函数定义了该类对象的操作即行为。值可以不同;成员函数定义了该类对象的操作即行为。v类具有对数据的类具有对数据的抽象性抽象性、隐藏性隐藏性和和封装性封装性。v类对象的行为由类的内部数据结构和相关的操作确定;类对象的行为由类的内部数据结构和相关的操作确定;外部行为通过操作外部行为通过操作接口接口实现。人们关心的就是操作接口所实现。人们关心的就是操作接口所能提供的服务
4、。能提供的服务。例例 3.1 3.1 类类The person watching TV does not need to know the TV sets structure and how it works inside.But he needs to control the TV set.TV needs to provide an interface.Internal State对象及其信息封装的例子对象及其信息封装的例子:TV开请求开请求变更频道变更频道调节声音调节声音Turn OnTurn OffChange ChannelAdjust VolumeOthers.被封装的行为和状态被
5、封装的行为和状态关请求关请求class 类名类名private:/私有数据成员和成员函数私有数据成员和成员函数public:/公有数据成员和成员函数公有数据成员和成员函数protected:/保护的数据成员和成员函数保护的数据成员和成员函数;3.1.1 3.1.1 类的定义类的定义 (1)(1)class 是关键字,类名是一个标识符,必须符合是关键字,类名是一个标识符,必须符合C+C+标标识符的命名规则。识符的命名规则。内是类的定义体部分,说明该类的成员,内是类的定义体部分,说明该类的成员,类的成员包括数据成员和成员函数。类的成员包括数据成员和成员函数。(2)(2)类成员的三种访问控制权限类成
6、员的三种访问控制权限:类有三种访问控制权限,分别是类有三种访问控制权限,分别是private(私有成员)、(私有成员)、public(公有成员)、(公有成员)、protected(保护成员),在每一种访(保护成员),在每一种访问控制权限下,均可以定义数据成员和成员函数。问控制权限下,均可以定义数据成员和成员函数。:私有成员是在类中被隐藏的部分,它往往是用来描述该私有成员是在类中被隐藏的部分,它往往是用来描述该类对象属性的数据成员,私有成员只能由本类的成员函数或某些特类对象属性的数据成员,私有成员只能由本类的成员函数或某些特殊说明的函数(如第殊说明的函数(如第4 4章讲到的友员函数)访问,而类的
7、外部根本就章讲到的友员函数)访问,而类的外部根本就无法访问。实现了无法访问。实现了的有效控制,有利于的有效控制,有利于,使内部使内部数据不能被任意的访问和修改,使数据得到数据不能被任意的访问和修改,使数据得到,也不会对该也不会对该类以外的其余部分造成影响,使模块之间的相互作用被降低到最小类以外的其余部分造成影响,使模块之间的相互作用被降低到最小。(1)(1)class 是关键字,类名是一个标识符,必须符合是关键字,类名是一个标识符,必须符合C+C+标标识符的命名规则。识符的命名规则。内是类的定义体部分,说明该类的成员,内是类的定义体部分,说明该类的成员,类的成员包括数据成员和成员函数。类的成员
8、包括数据成员和成员函数。(2)(2)类成员的三种访问控制权限类成员的三种访问控制权限:类有三种访问控制权限,分别是类有三种访问控制权限,分别是private(私有成员)、(私有成员)、public(公有成员)、(公有成员)、protected(保护成员),在每一种访(保护成员),在每一种访问控制权限下,均可以定义数据成员和成员函数。问控制权限下,均可以定义数据成员和成员函数。publicprivatepublic (1)(1)class 是关键字,类名是一个标识符,必须符合是关键字,类名是一个标识符,必须符合C+C+标标识符的命名规则。识符的命名规则。内是类的定义体部分,说明该类的成员,内是类
9、的定义体部分,说明该类的成员,类的成员包括数据成员和成员函数。类的成员包括数据成员和成员函数。(2)(2)类成员的三种访问控制权限类成员的三种访问控制权限:类有三种访问控制权限,分别是类有三种访问控制权限,分别是private(私有成员)、(私有成员)、public(公有成员)、(公有成员)、protected(保护成员),在每一种访(保护成员),在每一种访问控制权限下,均可以定义数据成员和成员函数。问控制权限下,均可以定义数据成员和成员函数。protectedprivatepublicdata1data2function1data3data4function2function3functio
10、n4Member visibilitystrangerfriend (3)(3)由于类的公有成员提供了一个类的接口,所以一般情况下,由于类的公有成员提供了一个类的接口,所以一般情况下,先定义公有成员,再定义保护成员和私有成员,这样可以在阅先定义公有成员,再定义保护成员和私有成员,这样可以在阅读时首先了解这个类的接口。读时首先了解这个类的接口。类的设计是类的设计是以数据为中心,以数据为中心,还是还是以行为为中心?以行为为中心?class Aprivate:int i,j;float x,y;public:void func1(void);void func2(void);class Apubli
11、c:void func1(void);void func2(void);private:int i,j;float x,y;(4)(4)结构体结构体struct和类和类class的区别的区别 C C语言中的结构体只有数据成员,无函数成员。语言中的结构体只有数据成员,无函数成员。C+C+语言中的结构体可有数据成员和函数成员。语言中的结构体可有数据成员和函数成员。可以随意修改结构体变量中的数据,这样对数据的操作是很不可以随意修改结构体变量中的数据,这样对数据的操作是很不安全的,不能通过结构体对数据进行保护和控制;在结构体中,安全的,不能通过结构体对数据进行保护和控制;在结构体中,数据和其相应的操作
12、是分离的,使得程序的复杂性难以控制,而数据和其相应的操作是分离的,使得程序的复杂性难以控制,而且程序的可重用性不好,严重影响了软件的生产效率。且程序的可重用性不好,严重影响了软件的生产效率。所以,一般我们所以,一般我们使用使用,当,当使用使用。class Tdate /定义日期类定义日期类public:/定义公有成员函数定义公有成员函数 void Set(int m,int d,int y);/置日期值置日期值 int isLeapYear();/判是否闰年判是否闰年 void Print();/输出日期值输出日期值private:/定义私有数据成员定义私有数据成员 int month;int
13、 day;int year;/类定义体的结束类定义体的结束(5)(5)类定义举例类定义举例3.1.2 3.1.2 类中成员函数的定义类中成员函数的定义 成员函数是程序算法实现的部分,是对封装的数据进行操作成员函数是程序算法实现的部分,是对封装的数据进行操作的唯一途径。的唯一途径。类的成员函数有两种定义方法:外联定义和内联定义。类的成员函数有两种定义方法:外联定义和内联定义。1.1.外联函数是指在类定义体中外联函数是指在类定义体中声明声明成员函数,而在类外成员函数,而在类外定义定义成成员函数。员函数。作用域区分符作用域区分符 :指明一个成员函数或数据成员所在的类。指明一个成员函数或数据成员所在的
14、类。:前若不跟类名,则成为全局数据或全局函数前若不跟类名,则成为全局数据或全局函数(非成员函数非成员函数)。class Tdate public:void Set(int m,int d,int y);int isLeapYear();void Print();private:int month;int day;int year;void Tdate:Set(int m,int d,int y)month=m;day=d;year=y;int Tdate:IsLeapYear()return(year%4=0&year%100!=0)|(year%400=0);void Tdate:Print
15、()coutmonth/day /year公有成员公有成员或或 (*对象指针名对象指针名).公有成员公有成员(3)通过对象的引用调用成员通过对象的引用调用成员格式:对象的引用格式:对象的引用.公有成员公有成员 class Myclock private:int hour,minute,second;public:void init();void updata();void display();Myclock clock,*pclock;/定义对象clock和指向Myclock类对象的指针pclockclock.init();/通过对象访问公有成员函数通过对象访问公有成员函数pclock=&cl
16、ock;/指针指针pclockpclock指向对象指向对象clockclockpclock-display();/通过指针访问成员函数通过指针访问成员函数clock.hour=4;/错误,因为对象不能访问其私有成员错误,因为对象不能访问其私有成员clock.hour=4;错误,因为对象不能访问其私有成员错误,因为对象不能访问其私有成员在调用成员函数时,通常使用缩写形式,如上例中的语句在调用成员函数时,通常使用缩写形式,如上例中的语句s.sets.set(2,15,1998);(2,15,1998);就是就是s.s.TdateTdate:set(2,15,1998);set(2,15,1998)
17、;的缩写,因的缩写,因此可以定义两个或多个类的具有相同名字的成员而不会产生二义此可以定义两个或多个类的具有相同名字的成员而不会产生二义性。性。当一个成员函数被调用时,系统自动向它传递一个隐含的参当一个成员函数被调用时,系统自动向它传递一个隐含的参数,该参数是一个数,该参数是一个,在程序中,在程序中可以使用关键字可以使用关键字thisthis来引用该指针,因此称该指针为来引用该指针,因此称该指针为thisthis指针。指针。,它将成员和用于操作这些,它将成员和用于操作这些成员的成员函数连接在一起。成员的成员函数连接在一起。3.2.4 3.2.4 名字解析和this指针void Tdate:set
18、(int m,int d,int y)/置日期值置日期值 month=m;day=d;year=y;void Tdate:set(int m,int d,int y)/置日期值置日期值 this-month=m;this-day=d;this-year=y;使用this指针,保证了每个对象可以拥有不同的数据成员,但处理这些数据成员的代码可以被所有的对象共享。thisthis指针指针同普通函数一样,类的成员函数也可以是带缺省同普通函数一样,类的成员函数也可以是带缺省值的函数,其调用规则同普通函数。成员函数也可值的函数,其调用规则同普通函数。成员函数也可以是重载函数,类的成员函数的重载与全局函数的
19、以是重载函数,类的成员函数的重载与全局函数的重载方法相同。重载方法相同。【例例3.4】带缺省参数的成员函数。带缺省参数的成员函数。【例例3.5】重载成员函数。重载成员函数。3.2.5 3.2.5 带缺省参数的成员函数和重载成员函数带缺省参数的成员函数和重载成员函数5047 /tdate.h 这个头文件只存放有关Tdate类的定义说明#ifndef Tdate_H /用来避免重复定义#define Tdate_H /不是类的一部分class Tdatepublic:void set(int,int,int);/成员函数原型int isLeapYear();/成员函数原型void print();
20、/成员函数原型private:int month;int day;int year;/Tdate类定义的结束#endif /Tdate_H1.1.定义头文件:包含类的说明,内联函数的原型和定义定义头文件:包含类的说明,内联函数的原型和定义前两行的作用:如果一个程序系统中的多个文件均包含前两行的作用:如果一个程序系统中的多个文件均包含TdateTdate类,类,则在编译时可避免则在编译时可避免TdateTdate类中标识符的重复定义。如果已定义,可类中标识符的重复定义。如果已定义,可使编译器跳过使编译器跳过#endif#endif之前的所有行。之前的所有行。/tdate.cpp 类Tdate的实
21、现部分#include /*因为tdate.cpp文件要访问运算符 和ostream类对象cout,而这二者都是定义在iostream类中的,所以包含iostream头文件*/#includetdate.h /包含用户自定义的头文件,该文件中提供了Tdate类的定义using namespace std;void Tdate:set(int m,int d,int y)month=m;day=d;year=y;int Tdate:isLeapYear()return(year%4=0&year%100!=0)|(year%400=0);void Tdate:print()cout month
22、/day /year endl;2.2.类的实现:包含类中各成员函数的定义(可为每个类建一个类的实现:包含类中各成员函数的定义(可为每个类建一个cppcpp文件)文件)/ch3_2.cpp#include#includetdate.husing namespace std;void someFunc(Tdate&refs)refs.print();/通过对象的引用调用成员函数if(refs.isLeapYear()cout errorn;/通过对象的引用调用成员函数else cout print();/通过指向对象的指针调用成员函数 if(*pTdate).isLeapYear()cout e
23、rrorn;/通过指向对象的指针调用成员函数 else cout rightn;someFunc(s);/对象的地址传给引用 return 0;3.3.类的应用:包含类的应用:包含main()main()函数,在其中使用不同的类函数,在其中使用不同的类3.3 3.3 构造函数和析构函数构造函数和析构函数对象的初始化是指对象成员数据的初始化,在使用对象对象的初始化是指对象成员数据的初始化,在使用对象前,一定要初始化。由于成员数据一般为私有的前,一定要初始化。由于成员数据一般为私有的(),所以不能直接赋值。所以不能直接赋值。对象成员数据初始化有以下两种方法:对象成员数据初始化有以下两种方法:1 1
24、、在类中、在类中提供一个普通成员函数来初始化提供一个普通成员函数来初始化2 2、使用构造函数使用构造函数3.3.1 3.3.1 构造函数构造函数 606 26 4 class Circle private:struct point int x,y;center;int radius;public:Circle()center.x=0;center.y=0;radius=0;Circle(int,int,int);double getArea();double getDistance();void display();/用户自定义的默认构造函数用户自定义的默认构造函数/构造函数构造函数Circl
25、e:Circle(int x,int y,int r)center.x=x;center.y=y;radius=r;/构造函数的定义构造函数的定义 circle.cpp void main()Circle c1;c1.display();coutthe distance from c1 to 0:c1.getDistance()endl;coutthe Area of c1:c1.getArea()endl;Circle c2(3,4,1);c2.display();coutthe distance from c2 to 0:“c2.getDistance()endl;coutthe Area
26、 of c2:c2.getArea()endl;/调用默认构造函数调用默认构造函数/调用构造函数,注意调用格式调用构造函数,注意调用格式6 5 对象数组的初始化对象数组的初始化 当对象数组所属类含有当对象数组所属类含有时时,(1),(1)可可用初始化列表用初始化列表按顺序调用构造函数按顺序调用构造函数初始化对象数组的每个元素,如初始化对象数组的每个元素,如:Point Triangle3=Point(0,0),Point(5,5),Point(10,0);Point Rectangle22=Point(0,0),Point(0,6),Point(16,6),Point(16,0);(2)(2)
27、也可以也可以先定义后给每个元素赋值先定义后给每个元素赋值,其赋值格式为其赋值格式为:对象数组名对象数组名行下标行下标 列下标列下标=构造函数名构造函数名(实参表实参表);例如例如:Rectangle00=Point(0,0);Rectangle01=Point(0,6);Rectangle10=Point(16,6);Rectangle11=Point(16,0);6 7 void main()Circle array2;array0.display();coutthe distance from c1 to:“array0.getDistance()endl;coutthe Area of
28、c1:array0.getArea()endl;array1=Circle(3,4,1);array1.display();coutthe distance from c2 to 0:“array1=.getDistance()endl;coutthe Area of c2:array1=.getArea()endl;/调用两次默认构造函数,对两个数组元素初始化调用两次默认构造函数,对两个数组元素初始化/调用构造函数,注意调用格式调用构造函数,注意调用格式/定义对象数组定义对象数组 例:用构造函数创建例:用构造函数创建d1,并用,并用d1初始化新创建的对象初始化新创建的对象d2。Tdate d
29、1(3,1,2002);Tdate d2(d1);class cat private:int age;float weight;char *color;public:cat();cat(cat&);void play();void hunt();cat:cat(cat&other)age=other.age;weight=other.weight;color=other.color;/默认构造函数默认构造函数/拷贝构造函数的声明拷贝构造函数的声明/拷贝构造函数的定义(外联)拷贝构造函数的定义(外联)#includeusing namespace std;class CExampleprivat
30、e:int a;public:CExample(int b)a=b;void show()coutaendl;从以上代码的运行结果可以看出,从以上代码的运行结果可以看出,系统为对象系统为对象B分配了内存并通过系统分配了内存并通过系统提供的提供的默认拷贝构造函数默认拷贝构造函数完成了与完成了与对象对象A的复制过程。的复制过程。void main()CExample A(100);CExample B(A);B.show();#includeusing namespace std;class CExample private:int a;public:CExample(int b)a=b;CExa
31、mple(const CExample&C)a=C.a;void show()coutaendl;void main()CExample A(100);CExample B=A;B.show();CExample(const CExample&C)是是自定义的拷贝构造函数自定义的拷贝构造函数。函数的名称。函数的名称和类名一致,它的唯一的一个参数是本和类名一致,它的唯一的一个参数是本类型的一个引用变量,该参数是类型的一个引用变量,该参数是const类型,不可变的。类型,不可变的。int*p1=new int(1);int*p2=p1;delete p1;coutV_p1:*p1endl;cout
32、V_p2:*p2endl;coutD_p1:p1endl;coutD_p2:p2endl;delete p2;/:只有只有p1的值被复制到的值被复制到p2,结果是两,结果是两 个指针指向堆中的同一块空间个指针指向堆中的同一块空间。/由于由于p1被删除,被删除,*p1和和 *p2均为无意义的值。均为无意义的值。/p1 和和p2悬空,但是其值悬空,但是其值 依然存在。依然存在。/由于由于p1被删除,被删除,p2就不能再被删除,否则就不能再被删除,否则 同一空间被释放两次,会出现运行时错误。同一空间被释放两次,会出现运行时错误。int*p1=new int(1);int*p2=p1;delete p
33、1;coutV_p1:*p1endl;coutV_p2:*p2endl;coutD_p1:p1endl;coutD_p2:p2endl;delete p2;/:复制复制对象,两个指针对象,两个指针 p1 和和p3都分别指向堆中都分别指向堆中 自己唯一的一块空间自己唯一的一块空间。/p1被删除,对被删除,对p3无影响,无影响,*p3仍然存在。仍然存在。/p1 和和p2地址值相同,但地址值相同,但 p3是独立的唯一的地址。是独立的唯一的地址。/释放释放p3是正确的。是正确的。int*p3=new int(*p1);coutV_p3:*p3endl;cout“D_p3:p3endl;delete p
34、3;7 0:():(),1),()n)Circle()center.x=0;center.y=0;radius=1;Circle():radius(1)center.x=0;center.y=0;class Circle private:struct point int x,y;center;int radius;const double PI;public:Circle():radius(1),PI(3.1415926)center.x=0;center.y=0;Circle:Circle(int x,int y,int r):PI(3.1415926)center.x=x;center.y
35、=y;radius=r;error C2758:PI:must be initialized in constructor base/member initializer list7 2Person(string s);class Personpublic:Person(string n)name=n;void printname()coutnamePerson/注意:注意:f函数的参数类型为函数的参数类型为Person类类/不需类型转换,不需类型转换,p1本身既为本身既为Person类型类型/隐式调用转换构造函数,将隐式调用转换构造函数,将p2转换为转换为Person类型类型/调用构造函数调
36、用构造函数class Personpublic:Person(string n,int i)name=n;id=i;Person(string n)name=n;void print()coutname,idendl;private:string name;int id;void f(Person p)p.print();void main()Person p1(zhang,1001);f(p1);string p2=wang;f(p2);/当数据成员多于一个时,用这当数据成员多于一个时,用这 种隐式转换就会出现问题。种隐式转换就会出现问题。/有两个参数的构造函数有两个参数的构造函数/有一个参数的转换有一个参数的转换构造函数构造函数explicit Person(string n)name=n;/类型不匹配,但无编译错误,运行结果出现无意义数据类型不匹配,但无编译错误,运行结果出现无意义数据/出现编译期错误出现编译期错误 7 6;/例例:在在Circle类中用于对实例圆进行计数类中用于对实例圆进行计数(Circle.h)/在全局空间中定义静态数据成员在全局空间中定义静态数据成员(Circle.cpp)8 18 2 coutCircle:getinstanceCounter();/8 3