《第9章_类与对象.ppt》由会员分享,可在线阅读,更多相关《第9章_类与对象.ppt(83页珍藏版)》请在taowenge.com淘文阁网|工程机械CAD图纸|机械工程制图|CAD装配图下载|SolidWorks_CaTia_CAD_UG_PROE_设计图分享下载上搜索。
1、1第9章 类与对象2第9章 类与对象9.1 类的定义和声明9.2 对象的定义和使用9.3 构造函数和析构函数第9章 类与对象3面向对象的方法面向对象的方法目的:实现软件设计的产业化。观点:自然界是由实体(对象)所组成,使用面向对象的观点来描述、模仿并处理现实问题。要求:高度的概括、分类、抽象。对象、属性、行为抽象:对具体的对象进行概括,抽出这一类对象的公共性质并加以描述的过程。封装:将对象的内部实现和外部行为分隔开来。49.1 类的定义和声明类(class)是用户自定义数据类型。如果程序中要使用类类型(class type),必须根据实际需要定义,或者使用已设计好的类。C+定义一个类,其方法与
2、定义一个结构体类型是相似的,一般形式为:class 类名 /类体成员列表;59.1.1 类的定义类成员可以是数据或函数。所有成员必须在类的内部声明,一旦类定义完成后,就没有任何其他方式可以再增加成员了。69.1.1 类的定义类定义时必须给出各个数据成员(data member)的数据类型声明,其一般形式为:class 类名 /类体 数据成员类型数据成员类型 数据成员名列表数据成员名列表;/数据成员声明数据成员声明;79.1.1 类的定义每个类可以包含成员函数,能够访问类自身的所有成员。在类体内部,定义成员函数的方法有两种:89.1.1 类的定义成员函数定义(也是声明)在类定义中,形式如下:cl
3、ass 类名类名 /类体类体 返回类型返回类型 函数名函数名(形式参数列表形式参数列表)/成员函数定义成员函数定义 函数体函数体 ;99.1.1 类的定义成员函数声明在类中,定义在类外部,形式:class 类名类名 /类体类体 返回类型返回类型 函数名函数名(类型类型1 参数名参数名1,类型类型2 参数名参数名2,);返回类型返回类型 类名类名:函数名函数名(形式参数列表形式参数列表)函数体函数体109.1.1 类的定义class Data /Data类定义类定义 void set(int d);int get()return data;int data;/数据成员数据成员;void Data
4、:set(int d)data=d;/访问类的数据成员访问类的数据成员119.1.1 类的定义类定义一般放在程序文件开头,或者放到头文件中被程序文件包含,此时这个定义是全局的。在全局作用域内,该定义处处可见,因此同作用域内的所有函数都可以使用它。类定义也可以放到函数内部或局部作用域中,此时这个定义是局部的。若在函数内部有同名的类定义,则全局声明在该函数内部是无效的,有效的是局部定义的。一般地,由于类是为整个程序服务的,因此很少有将类放到局部作用域中去定义。129.1.2 成员访问控制对类的成员进行访问,来自两个访问源:类成员和类用户。类成员指类本身的成员函数,类用户指类外部的使用者,包括全局函
5、数、另一个类的成员函数等。139.1.2 成员访问控制无论数据成员还是函数成员,类的每个成员都有访问控制属性:public(公有的)、private(私有的)和protected(保护的)。公有成员用public标号声明,类成员和类用户都可以访问公有成员,任何一个来自类外部的类用户都必须通过公有成员来访问。显然,public实现了类的外部接口。149.1.2 成员访问控制私有成员用private标号声明,只有类成员可以访问私有成员,类用户的访问是不允许的。显然,private实现了私有成员的隐蔽。保护成员用protected标号声明,在不考虑继承的情况下,protected的性质和privat
6、e的性质一致,但保护成员可以被派生类的类成员访问。159.1.2 成员访问控制成员访问控制是C+的类和结构体又一个重要特性。加上访问标号,类定义更一般的形式为:class 类名 /类体public:/公有访问权限 公有的数据成员和成员函数protected:/保护访问权限 保护的数据成员和成员函数private:/私有访问权限 私有的数据成员和成员函数;169.1.2 成员访问控制如果没有声明访问控制属性,类所有成员默认为private,即私有的。例如:class Data int a,b;/默认为私有的,外部不能直接访问默认为私有的,外部不能直接访问public:void set(int i
7、,int j,int k,int l,int m,int n)a=i,b=j,c=k,d=l,e=m,f=n;protected:/保护的,外部不能直接访问保护的,外部不能直接访问 int c,d;private:/私有的,外部不能直接访问私有的,外部不能直接访问 int e,f;Data d1;d1.set(1,2,3,4,5,6);d1.a=100;/错误错误 d1.c=20;/错误错误179.1.2 成员访问控制Data类的数据成员均是外部不能直接访问的,这使得类的数据被“隐蔽”起来,是“安全的”。但成员函数set是公有的,外部可以直接访问。从以上代码可知,通过set设置了数据成员,因此
8、set成员函数是外部访问数据成员的“接口”。实际编程中,为了使程序清晰,每一种成员访问限定符在类体中只出现一次,且按照public、protected、private顺序组织,形成访问权限层次分明的结构。189.1.3 类的数据成员1在类中声明数据成员类的数据成员可以是基本类型、数组、指针、引用、共用体、枚举类型、void指针、const限定等数据类型。例如:class ADT /类成员数据类型类成员数据类型 /成员函数成员函数 long color;double x,y,z,side;/基本类型基本类型 int a10;/数组数组 char*s;/指针指针 char&r;/引用引用 void
9、*p;/void指针指针;199.1.3 类的数据成员2在类中定义或声明数据类型 除了定义数据和成员函数之外,类还可以定义自己的局部类型。在类中定义或声明的数据类型的作用域是类内部,因此,它们不能在类外部使用。209.1.3 类的数据成员class ADT /类定义类定义struct Point int x,y;/定义结构体定义结构体union UData Point p;long color;/定义共用体定义共用体enum COLORS RED,GREEN,BLUE,BLACK,WHITE;/定定义义枚枚举类型举类型class Nested /嵌套类定义嵌套类定义/成员函数成员函数Point
10、 start;/数据成员数据成员UData end;/数据成员数据成员COLORS color;/数据成员数据成员;/成员函数成员函数/数据成员数据成员;/类定义结束类定义结束 219.1.4 类的成员函数1在类的外部定义成员函数如果成员函数仅有声明在类定义中,则在类外部必须有它的实现,其一般形式为:返回类型返回类型 类名类名:函数名函数名(形式参数列表形式参数列表)函数体函数体 229.1.4 类的成员函数例如:1 class Data /Data类定义类定义2 public:3void set(int d);/成员函数原型声明成员函数原型声明4 int get()5 return data
11、;6 7 private:8 int data;/数据成员数据成员9 ;239.1.4 类的成员函数10 void Data:set(int d)/使用使用 Data:限定限定11 12 data=d;/访问类的数据成员访问类的数据成员13 14 void set(int d)/全局普通函数全局普通函数15 16 data=d;/错误错误17 249.1.4 类的成员函数2内联成员函数 类的成员函数可以指定为inline,即内联函数。默认情况下,在类体中定义的成员函数若不包括循环等控制结构,符合内联函数要求时,C+会自动将它们作为内联函数处理(隐式inline)。259.1.4 类的成员函数可
12、以显式地将成员函数声明为inline。例如:class Data /Data类定义类定义int getx()return x;/内联成员函数内联成员函数inline int gety()return y;/显式指定内联成员函显式指定内联成员函数数inline void setxy(int _x,int _y);/显式指定内联成显式指定内联成员函数员函数void display();int x,y;inline void Data:setxy(int _x,int _y)/内联成员函数内联成员函数x=_x,y=_y;void Data:display()/非内联成员函数非内联成员函数/函数体函数
13、体 269.1.4 类的成员函数总结下来,成员函数是否内联的,有以下几条:符合内联函数要求;符合的条件,并且在类体中定义,自动成为内联的;符合的条件,在类体显式指明inline,或在外部定义时显式指明inline,或者同时显式指明,则函数是内联的;在类外部定义,并且既没有在类体,也没有在外部定义时显式指明inline,则函数不是内联的。279.1.4 类的成员函数3成员函数重载及默认参数 对成员函数重载或使用默认参数。例如:class MAX int Max(int x,int y)return xy?x:y;int Max()return Max(Max(a,b),Max(c,d);/重载重
14、载Maxint Set(int i=1,int j=2,int k=3,int l=4)a=i,b=j,c=k,d=l;/默认参数默认参数int a,b,c,d;/数据成员数据成员;289.1.4 类的成员函数 需要注意,声明成员函数的多个重载版本或指定成员函数的默认参数,只能在类内部中进行。299.1.4 类的成员函数4成员函数的存储方式用类实例化一个对象时,系统会为每一个对象分配存储空问。如果一个类包括了数据成员和函数成员,则要分别为数据和函数的代码分配存储空间。通常,C+会为每个对象的数据成员分配各自独立的存储空间,像第8章讲到的结构体成员那样。309.1.4 类的成员函数那么在类中的成
15、员函数是否会如图9.1(a)所示那样也分配各自独立的存储空间呢?319.1.4 类的成员函数一般情况下,不同对象的数据成员是不相同的,因而需要不同的空间存储它们;而不论调用哪一个对象的成员函数,实际调用的都是同样内容的代码。因此,若像图9.1(a)那样存放相同代码的多份副本,既浪费空间又无必要。实际上,C+每个对象所占用的存储空间只是该对象的数据成员所占用的存储空间,而不包括成员函数所占用的存储空间。成员函数代码只有公用的一个,调用不同对象的成员函数时都是执行同一段函数代码。329.1.4 类的成员函数图9.1 成员函数的存储方式339.1.4 类的成员函数例如定义了一个类 sizeof(Ti
16、me)的值是12。显然,Time类的存储空间长度只取决于数据成员h、m、s所占的空间,而与成员函数settime无关。C+把成员函数的代码存储在对象空间之外的地方。class Time /Time类类int h,m,s;/数据成员数据成员 void settime(int a,int b,int c)h=a,m=b,s=c;/成员成员 函数函数;9.2 对象的定义和使用定义一个类时,也就是定义了一个具体的数据类型。若要使用类,需要将类实例化,即定义该类的对象。34359.2.1 对象的定义1先定义类类型再定义对象假定事先已经定义了类类型,可以用它来定义类的对象,有两种形式为:将类的名字直接用作
17、类型名:指定关键字class或struct,后面跟着类的名字:类名类名 对象名列表对象名列表;class 类名类名 对象名列表对象名列表;或或 struct 类名类名 对象名列表对象名列表;Point a,b;/C+特色定义对象特色定义对象class Point x,y;/兼容兼容C语言特色定义对象语言特色定义对象 369.2.1 对象的定义2定义类类型的同时定义对象 一般形式为:例如:class 类名类名 /类体类体成员列表成员列表 对象名列表对象名列表;class Point /类体类体public:/公有的数据成员和成员函数公有的数据成员和成员函数private:/私有的数据成员和成员函
18、数私有的数据成员和成员函数 one,two;/对象列表对象列表 379.2.2 对象的动态建立和释放有时人们希望在需要用到对象时才创建(create)对象,在不需要用该对象时就撤销(destroy)它,释放其所占的存储空间,从而提高存储空间的利用率。利用第7章介绍的new运算符可以动态地分配对象空间,delete运算符释放对象空间。389.2.3 对象成员的引用访问对象中的成员可以有3种方法:399.2.3 对象成员的引用1通过对象名访问对象中的成员访问对象中数据成员的一般形式为:调用对象中成员函数的一般形式为:需要注意,从类外部只能访问类公有的成员。在一个类中应当至少有一个公有的成员函数,作
19、为外部访问类的接口,否则程序无法对类的对象进行任何操作。对象名对象名.成员名成员名对象名对象名.成员函数(实参列表)成员函数(实参列表)409.2.3 对象成员的引用例如已定义一个类:例如已定义一个类:class Data /Data类类public:int data;void fun(int a,int b,int d);private:void add(int m)data+=m;int x,y;419.2.3 对象成员的引用则函数 void caller1()Data A,B;/定义对象定义对象A.fun(1,2,3);/正确,类外部可以访问类的正确,类外部可以访问类的public成员函
20、数成员函数A.data=100;/正确,类外部可以访问类的正确,类外部可以访问类的public数据成员数据成员A.add(5);/错误错误,类外部不能访问类任何,类外部不能访问类任何private的成员的成员A.x=A.y=1;/错误错误,类外部不能访问类任何,类外部不能访问类任何private的成员的成员B.data=101;/正正确确,A.data和和B.data是是两两个个对对象象的的数数据据成成员,为不同的存储空间员,为不同的存储空间B.fun(4,5,6);/正正确确,A.fun和和B.fun函函数数调调用用相相同同的的代代码码,但作用不同的数据成员但作用不同的数据成员429.2.3
21、 对象成员的引用2通过对象指针访问对象中的成员访问对象中数据成员的一般形式为:调用对象中成员函数的一般形式为:对象指针对象指针成员名成员名对象指针对象指针成员函数(实参列表)成员函数(实参列表)439.2.3 对象成员的引用则函数 void caller1()Data A,*p,*p1;/定义对象指针变量定义对象指针变量p1=&A;/p1指向对象指向对象Ap1-data=100;/正确,类外部可以访问类的正确,类外部可以访问类的public数据成员数据成员p1-fun(1,2,3);/正确,类外部可以访问类的正确,类外部可以访问类的public成员函成员函数数p=new Data;/动态分配动
22、态分配Data对象对象p-data=100;/正确,类外部可以访问类的正确,类外部可以访问类的public数据成员数据成员p-fun(1,2,3);/正确,类外部可以访问类的正确,类外部可以访问类的public成员函成员函数数delete p;/撤销撤销p所指向的所指向的Data对象对象449.2.3 对象成员的引用3通过引用变量访问对象中的成员访问对象中数据成员的一般形式为:调用对象中成员函数的一般形式为:对象引用变量名.成员名对象引用变量名.成员函数(实参列表)459.2.3 对象成员的引用则函数 void caller1()Data A,&r=A;/定义对象引用变量定义对象引用变量r.d
23、ata=100;/正确,类外部可以访问类的正确,类外部可以访问类的public数据成员数据成员r.fun(1,2,3);/正确,类外部可以访问类的正确,类外部可以访问类的public成员函数成员函数469.2.3 对象成员的引用4对象的赋值如果一个类定义了两个或多个对象,则这些同类的对象之间可以互相赋值,或者说,一个对象的“值”可以赋给另一个同类的对象。这里所指的对象的“值”是指对象中所有数据成员的值。对象赋值的一般形式为:对象名1=对象名2Data a,b;a.data=100;b=a;479.2.3 对象成员的引用5对象、对象指针、对象引用作为函数的参数函数的参数可以是对象、对象指针或对象
24、引用。(1)当形参是对象时,实参要求是同类的对象名。(2)当形参是对象指针时,实参要求是同类对象的地址。(3)当形参是对象引用时,实参要求是同类的对象名。9.2.3 对象成员的引用48class Data /Data类类public:int data;void fun(int a,int b,int d);private:void add(int m)data+=m;int x,y;例如:499.2.3 对象成员的引用void func1(Data a,Data*p,Data&r)a.data=100;p-data=200;r.data=300;void caller4()Data A,B,C
25、;A.fun(1,2,3);B.fun(4,5,6);C.fun(7,8,9);func1(A,&B,C);/将将对对象象A、对对象象B的的地地址址、对对象象C的的引引用用传传递到函数递到函数func1509.2.3 对象成员的引用6函数返回值是对象、对象指针、对象引用函数返回对象时,将其内存单元的所有内容复制到一个临时对象中。函数返回对象指针或引用,本质上返回的是对象的地址而不是它的存储内容。519.2.3 对象成员的引用例如:Data func1()Data a;a.fun(1,2,3);return a;/可以返回局部对象,因为它被复制返回可以返回局部对象,因为它被复制返回Data*fu
26、nc2(Data*p1,Data*p2)if(p1-data p2-data)return p1;return p2;Data&func3(Data&r1,Data&r2)if(r1.data r2.data)return r1;return r2;529.2.3 对象成员的引用 void caller()Data A,B,C;A.fun(1,2,3);B.fun(4,5,6);C=func1();func2(&A,&B)-data=100;/等等 价价 于于 (&B)-data=100;func3(A,B).data=100;/等价于等价于 B.data=100;539.3.1 构造函数1对
27、象的初始化在建立一个对象时,通常最需要立即做的工作是初始化对象,如对数据成员赋初值。类的数据成员是不能在类定义时初始化的,例如:class Point /Point类类int x=0,y=0;/错误错误/其他成员其他成员549.3.1 构造函数2构造函数的定义C+提供了构造函数(constructor)来处理对象的初始化。构造函数是类的一种特殊成员函数,不需要人为调用,而是在建立对象时自动被执行。559.3.1 构造函数C+规定构造函数的名字与类的名字相同,并且不能指定返回类型。定义形式为:类名类名(形式参数列表形式参数列表)函数体函数体569.3.1 构造函数与其他任何函数一样,构造函数可以
28、声明为内联的。对于有参数的构造函数,定义对象的一般形式为:对于无参数的构造函数,定义对象的一般形式为:类名类名 对象名对象名1(1(实参列表实参列表),),对象名对象名2(2(实参列表实参列表),.;),.;类名类名 对象名对象名1,1,对象名对象名2,.;2,.;579.3.1 构造函数例9.1 有两个长方体,其长、宽、高分别为1、2、3和10、20、30.分别求它们的体积。设计一个长方体类,在类中使用带参数的构造函数。1#include 2 using namespace std;3 class Cuboid /Cuboid类表示长方体类表示长方体 4 public:5 Cuboid(in
29、t l,int h,int d);/构造函数构造函数 6 int volumn()return length*height*depth;7 private:8 int length,height,depth;9 ;589.3.1 构造函数 10 Cuboid:Cuboid(int l,int h,int d)/外外部部定定义义的的构构造造函数函数 11 12 length=l,height=h,depth=d;/初始化数据成员初始化数据成员 13 coutCuboid:L=l,H=h,D=dendl;14 15 int main()16 17 Cuboid a(1,2,3);/调用构造函数初始
30、化调用构造函数初始化a 18 coutvolumn=a.volumn()endl;/输出体积输出体积 19 Cuboid b(10,20,30);/调用构造函数初始化调用构造函数初始化b 20 coutvolumn=b.volumn()endl;/输出体积输出体积 21 return 0;22 599.3.1 构造函数关于构造函数的说明:(1)构造函数是在创建对象时自动执行的,而且只执行一次,并先于其他成员函数执行。构造函数不需要人为调用,也不能被人为调用。(2)构造函数一般声明为公有的(public),因为创建对象通常是在类的外部进行的。如果构造函数声明为保护的(protected)或私有的
31、(protected),那就意味着在类外部创建对象(并调用构造函数)是错误的。换言之,这样的类是不能由外部实例化,只能由类内部(一般是静态成员函数)实例化,这种情况不是通常的做法。609.3.1 构造函数(3)在构造函数的函数体中不仅可以对数据成员初始化,而且可以包含任意其他功能的语句,例如分配动态内存等,但是一般不提倡在构造函数中加入与初始化无关的内容。(4)带参数的构造函数中的形参,是在定义对象时由对应的实参给定的,用这种方法可以方便地实现对不同对象进行不同的初始化。需要注意,实参必须与构造函数的形参的个数、次序、类型一致。回顾类的定义成员的访问控制定义对象对象成员的引用对象的初始化构造函
32、数61class Time public:void set_time(int h,int m,int s);hour=h,minute=m,sec=s;void show_time();private:int hour,minute,sec;void Time:show_time()couthour“:”minute“:”secendl;int main()Time t1,t2;t1.set_time(1,2,3);t1.show_time();t2.set_time(4,5,6);t2.show_time();return 0;Time(int h,int m,int s)hour=h,mi
33、nute=m,sec=s;Time t1(1,2,3),t2(4,5,6);629.3.1 构造函数3构造函数初始化列表与其他函数不同的是,构造函数可以包含一个构造函数初始化列表有两种定义形式:/第第1种形式种形式类名类名(形式参数列表形式参数列表):构造函数初始化列表构造函数初始化列表函数体函数体/第第2 2种形式种形式类名类名(形式参数列表形式参数列表):):构造函数初构造函数初始化列表始化列表 函数体函数体 9.3.1 构造函数63Cuboid:Cuboid(int l,int h,int d):leight(l),height(h),depth(d)coutCuboid:L=l,H=h
34、,D=dendl;Cuboid:Cuboid(int l,int h,int d)length=l,height=h,depth=d;coutCuboid:L=l,H=h,D=dendl;等价于:649.3.2 构造函数的重载在一个类中可以定义多个构造函数版本,即构造函数允许被重载。659.3.2 构造函数的重载例9.2 平面上有两个点,其x、y坐标分别为0、0和1、2.编程显示坐标值。设计一个点类,设计带参数和不带参数两个版本的构造函数。1#include 2 using namespace std;3 class Point 4 public:5 Point()x=y=0;6 Point(
35、int a,int b):x(a),y(b)7 void display()coutx=x,y=yendl;8 private:9 int x,y;/坐标值坐标值 10 ;669.3.2 构造函数的重载例9.2 11 int main()12 13 Point m;/调用无参数版本的构造函数调用无参数版本的构造函数 14 m.display();15 Point n(1,2);/调用有参数版本的构造函数调用有参数版本的构造函数 16 n.display();17 return 0;18 尽管在一个类中可以包含多个构造函数,但是对于每一个对象来说,建立对象时只执行其中一个,并非每个构造函数都被执
36、行。679.3.3 带默认参数的构造函数构造函数的参数允许使用默认值。对类的设计者来说,使用默认参数可以减少代码重复;对类的使用者者来说,使用默认参数可以方便地用适当的参数进行初始化。689.3.3 带默认参数的构造函数例9.3 用带默认参数的构造函数改进例9.2 1#include 2 using namespace std;3 class Point 4 public:5 Point(int a=0,int b=0):x(a),y(b)6 void display()coutx=x,y=yendl;7 private:8 int x,y;9 ;10 int main()11 12 Poin
37、t k,m(1),n(1,2);/m点点的的坐坐标标为为(1,0),n为为(1,2)13 k.display();m.display();n.display();14 return 0;15 699.3.6 复制构造函数复制构造函数的功能是利用一个已知的对象来初始化一个被创建的同类的对象。只有单个形参,而且该形参是对本类类型对象的引用常量,这样的构造函数称为复制构造函数(copy constructor)。定义的一般形式为:类名类名(const(const 类名类名&obj)&obj)函数体函数体 709.3.6 复制构造函数例如:class Point public:Point():x(0)
38、,y(0)/默认构造函数默认构造函数 Point(const Point&r):x(r.x),y(r.y)/复制构复制构造函数造函数 Point(int a,int b):x(a),y(b)/带参数构造函数带参数构造函数private:int x,y;Point a;Point b(1,2);Point c(b);/调用复制构造函数调用复制构造函数719.3.6 复制构造函数1合成复制构造函数每个类必须有一个复制构造函数。如果类没有定义复制构造函数,编译器就会自动合成一个,称为合成复制构造函数(synthesized copy constructor)。729.3.6 复制构造函数2何时使用复
39、制构造函数以下3种情况会使用复制构造函数。(1)用一个对象显式或隐式初始化另一个对象时。C+支持两种初始化形式:复制初始化,使用等号(=);直接初始化,将初始化式放在圆括号中。复制初始化和直接初始化是有区别的:复制初始化总是调用复制构造函数,而直接初始化直接调用与实参匹配的构造函数。Point b(1,2);/调用构造函数调用构造函数Point c(b);/调用复制构造函数调用复制构造函数Point a=b;/调用复制构造函数调用复制构造函数739.3.6 复制构造函数(2)函数参数按值传递对象时或函数返回对象时。当函数形参为对象类型,函数调用按值传递对象,即编译器调用复制构造函数产生一个实参
40、对象副本传递到函数中。类似地,以对象类型作为返回值时,编译器调用复制构造函数产生一个return语句中的值的副本返回到调用函数。Point func(Point a)int main()Point A;Point b(a);func(A);return b;return 0;749.3.6 复制构造函数(3)根据元素初始化式列表初始化数组元素时。如果没有为类类型数组提供元素初始化式,则将用默认构造函数初始化每个元素。然而,如果使用常规的大括号的数组初值列表形式来初始化数组时,则使用复制初始化来初始化每个元素。总的来说,正是有了复制构造函数,函数才可以传递对象和返回对象,对象数组才能用初值列表的
41、形式初始化。759.3.8 析构函数析构函数(destructor)是类的另一个特殊成员函数,它的作用与构造函数相反,C+规定析构函数的名字是类名的前面加一个波浪号()。其定义形式为:析构函数不返回任何值,没有返回类型,也没有函数参数。由于没有函数参数,因此它不能被重载。换言之,一个类可以有多个构造函数,但是只能有一个析构函数。类名类名()函数体函数体 769.3.8 析构函数1何时调用析构函数(1)对象在程序运行超出其作用域时自动撤销,撤销时调用该对象的析构函数。(2)如果用new运算动态地建立了一个对象,那么用delete运算释放该对象时,调用该对象的析构函数。779.3.8 析构函数2合
42、成析构函数与复制构造函数不同,编译器总是会为类生成一个析构函数,称为合成析构函数。合成析构函数按对象创建时的逆序撤销每个非静态成员,即它是按成员在类中声明次序的逆序撤销成员的。对于类类型的每个成员,合成析构函数调用该成员的析构函数来撤销对象。需要注意,合成析构函数并不删除指针成员所指向的对象,它需要程序员显式编写析构函数去处理。789.3.8 析构函数3何时需要编写析构函数许多类不需要显式地编写析构函数,尤其是具有构造函数的类不一定需要定义自己的析构函数。析构函数通常用于释放在构造函数或在对象生命期内获取的资源(如动态分配的内存)。广义地讲,析构函数的作用并不仅限于释放资源方面,它可以执行任意
43、操作,用来执行“对象即将被撤销之前程序员所期待的任何操作”。如果类需要析构函数,则该类几乎必然需要定义自己的复制构造函数和赋值运算符重载,这个规则称为析构函数三法则(rule of three)。799.3.8 析构函数例9.4 设计一个类表示字符串,长度可以动态变化。1#include 2 using namespace std;3 class CString 4 public:5 CString(const char*str);/构造函数构造函数 6 CString();/析构函数析构函数 7 void show()coutpendl;8 private:9 char*p;10 ;11 C
44、String:CString(const char*str)12 13 p=new char strlen(str)+1;14 strcpy(p,str);809.3.8 析构函数例9.4续 15 cout构造:构造:strendl;16 17 CString:CString()18 19 cout析构:析构:pendl;20 delete p;/析构函数必须释放析构函数必须释放p占用的内存占用的内存 21 22 int main()23 24 CString s1(C+),s2=JavaScript;25 s1.show();s2.show();26 return 0;27 819.3.8 析构函数运行结果:构造:C+构造:JavaScriptC+JavaScript析构:JavaScript析构:C+829.3.9 构造函数和析构函数的调用次序在使用构造函数和析构函数时,需要特别注意对它们的调用时间和调用次序。可简述为:先构造的后析构,后构造的先析构。图9.7 对象构造函数和析构函数的调用次序83结束