1、第第9章章 关于类和对象的进一步讨论关于类和对象的进一步讨论9.1 构造函数构造函数9.2 析构函数析构函数9.3 调用构造函数和析构函数的顺序调用构造函数和析构函数的顺序9.4 对象数组对象数组9.5 对象指针对象指针9.6 共用数据的保护共用数据的保护9.7 对象的动态建立和释放对象的动态建立和释放9.8 对象的赋值和复制对象的赋值和复制9.9 静态成员静态成员9.10 友元友元9.11 类模板类模板在建立一个对象时在建立一个对象时,作某些初始化的工作如,作某些初始化的工作如对数据成对数据成员赋初值员赋初值。即在。即在创建对象(分配内存时)进行数据成创建对象(分配内存时)进行数据成员的初始
2、化员的初始化,因为对象是实实在在的对象,不能无具,因为对象是实实在在的对象,不能无具体属性值。体属性值。注意:注意:类的数据成员是不能在声明类时初始化的类的数据成员是不能在声明类时初始化的。9.1 构造函数构造函数作用:作用:创建对象(分配内存时)时进行数据创建对象(分配内存时)时进行数据成员的初始化成员的初始化9.1.1 对象的初始化对象的初始化如果一个类中所有的成员都是公用的,则可以在定义如果一个类中所有的成员都是公用的,则可以在定义对象时对数据成员进行初始化。如对象时对数据成员进行初始化。如class Timepublic:/声明为公用成员声明为公用成员 hour;minute;sec;
3、Time t1=14,56,30;/将将t1初始化为初始化为14:56:30但是,但是,一般数据成员是私有的,或者类中有一般数据成员是私有的,或者类中有private或或protected的成员,就不能用这种方法初始化的成员,就不能用这种方法初始化。如何实现?如何实现?C+提供了构造函数提供了构造函数(constructor)来处理对象的初始来处理对象的初始化。化。构造函数是特殊的成员函数,与其他成员函数不同,构造函数是特殊的成员函数,与其他成员函数不同,不需要用户来调用它,而是在建立对象时自动执行不需要用户来调用它,而是在建立对象时自动执行。构造函数的构造函数的名字必须与类名同名名字必须与类
4、名同名,而不能由用户任意,而不能由用户任意命名,以便编译系统能识别它并把它作为构造函数处命名,以便编译系统能识别它并把它作为构造函数处理。理。它不具有任何类型,不返回任何值它不具有任何类型,不返回任何值。构造函数的功能是由用户定义的,构造函数的功能是由用户定义的,用户根据初始化的用户根据初始化的要求设计函数体和函数参数要求设计函数体和函数参数。9.1.2 构造函数的作用构造函数的作用例例9.1 在例在例8.3基础上定义构造成员函数。基础上定义构造成员函数。#include using namespace std;class Timepublic:Time()/定义构造成员函数,函数名与类名相同
5、定义构造成员函数,函数名与类名相同hour=0;/利用构造函数对对象中的数据成员利用构造函数对对象中的数据成员赋初值赋初值minute=0;sec=0;void set_time();/函数声明函数声明void show_time();/函数声明函数声明private:int hour;/私有数据成员私有数据成员int minute;int sec;void Time set_time()/定义成员函数,向数据成员赋值定义成员函数,向数据成员赋值cinhour;cinminute;cinsec;void Time show_time()/定义成员函数,输出数据成员的值定义成员函数,输出数据成员
6、的值 couthour:minute:secendl;int main()Time t1;/建立对象建立对象t1,同时调用构造函数同时调用构造函数t1.Time()t1.set_time();/对对t1的数据成员赋值的数据成员赋值t1.show_time();/显示显示t1的数据成员的值的数据成员的值 Time t2;/建立对象建立对象t2,同时调用构造函数同时调用构造函数t2.Time()t2.show_time();/显示显示t2的数据成员的值的数据成员的值return 0;程序运行的情况为:程序运行的情况为:10 25 54 (从键盘输入新值赋给从键盘输入新值赋给t1的数据成员的数据成员
7、)10:25:54 (输出输出t1的时、分、秒值的时、分、秒值)0:0:0 (输出输出t2的时、分、秒值的时、分、秒值)也可以也可以在类外定义构造函数:在类外定义构造函数:Time Time()/要加上类名要加上类名Time和域限定和域限定 符符“”hour=0;minute=0;sec=0;有关构造函数的使用,有以下说明:有关构造函数的使用,有以下说明:(1)在类对象进入其作用域时调用构造函数。在类对象进入其作用域时调用构造函数。(2)构造函数没有返回值,因此也不需要在定义构造构造函数没有返回值,因此也不需要在定义构造函数时声明类型,这是它和一般函数的一个重要的不函数时声明类型,这是它和一般
8、函数的一个重要的不同之点。同之点。(3)构造函数构造函数不需用户调用不需用户调用,也不能被用户调用。,也不能被用户调用。(4)如果用户自己没有定义构造函数,则如果用户自己没有定义构造函数,则C+系统会系统会自动生成一个构造函数,只是这个构造函数的函数体自动生成一个构造函数,只是这个构造函数的函数体是空的,也没有参数,不执行初始化操作。是空的,也没有参数,不执行初始化操作。不带参数构造函数,不带参数构造函数,这种方式使该类的每一个对象都这种方式使该类的每一个对象都得到同一组初值。得到同一组初值。带参数的构造函数,用户希望对不同的对象赋予不同带参数的构造函数,用户希望对不同的对象赋予不同的初值的初
9、值。构造函数首部的一般格式:构造函数首部的一般格式:构造函数名构造函数名(类型类型 1 形参形参1,类型,类型2 形参形参2,)实参是在定义对象时给出的实参是在定义对象时给出的。定义对象的一般格式为定义对象的一般格式为:类名类名 对象名对象名(实参实参1,实参,实参2,);9.1.3 带参数的构造函数带参数的构造函数例例9.2 有两个长方柱,其长、宽、高分别为:有两个长方柱,其长、宽、高分别为:(1)12,20,25;(2)10,14,20。求它们的体积。编一个基。求它们的体积。编一个基于对象的程序,在类中用带参数的构造函数。于对象的程序,在类中用带参数的构造函数。#include using
10、 namespace std;class Boxpublic:Box(int,int,int);/声明带参数的构造函数声明带参数的构造函数int volume();/声明计算体积的函数声明计算体积的函数 private:int height;int width;int length;Box Box(int h,int w,int len)/在类外定义带参数的构造函数在类外定义带参数的构造函数height=h;width=w;length=len;int Box volume()/定义计算体积的函数定义计算体积的函数return(height*width*length);int main()Bo
11、x box1(12,25,30);/建立对象建立对象box1,并指定并指定box1长、宽、高的值长、宽、高的值coutThe volume of box1 is box1.volume()endl;Box box2(15,30,21);/建立对象建立对象box2,并指定并指定box2长、宽、长、宽、高的值高的值coutThe volume of box2 is box2.volume()endl;return 0;程序运行结果如下:程序运行结果如下:The volume of box1 is 9000The volume of box2 is 9450注意注意:带参数的构造函数中的形参,带参数
12、的构造函数中的形参,其对应的实参在定义对其对应的实参在定义对象时给定。象时给定。C+还提供另一种初始化数据成员的方法还提供另一种初始化数据成员的方法参数初参数初始化表始化表来实现对数据成员的初始化。来实现对数据成员的初始化。这种方法不在函数体内对数据成员初始化,而是在函这种方法不在函数体内对数据成员初始化,而是在函数首部实现。数首部实现。例如例例如例9.2中定义构造函数可以改用以中定义构造函数可以改用以下形式:下形式:Box Box(int h,int w,int len):height(h),width(w),length(len)这种写法方便、简练,尤其当需要初始化的数据成员这种写法方便、
13、简练,尤其当需要初始化的数据成员较多时更显其优越性。较多时更显其优越性。甚至可以直接在类体中甚至可以直接在类体中(而不而不是在类外是在类外)定义构造函数。定义构造函数。9.1.4 用参数初始化表对数据成员初始化用参数初始化表对数据成员初始化在一个类中可以定义多个构造函数,以便对类对象提在一个类中可以定义多个构造函数,以便对类对象提供不同的初始化的方法,供用户选用。供不同的初始化的方法,供用户选用。这些构造函数具有相同的名字,而参数的个数或参数这些构造函数具有相同的名字,而参数的个数或参数的类型不相同。这称为构造函数的重载。的类型不相同。这称为构造函数的重载。在第在第4章第章第4.6节中所介绍的
14、函数重载的知识也适用于节中所介绍的函数重载的知识也适用于构造函数。构造函数。通过下面的例子可以了解怎样应用构造函数的重载。通过下面的例子可以了解怎样应用构造函数的重载。9.1.5 构造函数的重载构造函数的重载例例9.3 在例在例9.2的基础上,定义两个构造函数,其中一的基础上,定义两个构造函数,其中一个无参数,一个有参数。个无参数,一个有参数。#include using namespace std;class Boxpublic:Box();/声明一个无参的构造函数声明一个无参的构造函数Box(int h,int w,int len):height(h),width(w),length(le
15、n)/声明一个有参的构造函数,用参数的初始化表对数据成员初始声明一个有参的构造函数,用参数的初始化表对数据成员初始化化int volume();private:int height;int width;int length;Box Box()/定义一个无参的构造函数定义一个无参的构造函数height=10;width=10;length=10;int Box volume()return(height*width*length);int main()Box box1;/建立对象建立对象box1,不指定实不指定实参参coutThe volume of box1 is box1.volume()e
16、ndl;Box box2(15,30,25);/建立对象建立对象box2,指定指定3个实参个实参coutThe volume of box2 is box2.volume()endl;return 0;在本程序中定义了两个重载的构造函数,在本程序中定义了两个重载的构造函数,其实还可以其实还可以定义其他重载构造函数定义其他重载构造函数。说明:说明:(1)调用构造函数时不必给出实参的构造函数,称为调用构造函数时不必给出实参的构造函数,称为默认构造函数默认构造函数(default constructor)。显然,无参的构显然,无参的构造函数属于默认构造函数。一个类只能有一个默认构造函数属于默认构造函
17、数。一个类只能有一个默认构造函数。造函数。(2)如果在建立对象时选用的是无参构造函数,应注如果在建立对象时选用的是无参构造函数,应注意正确书写定义对象的语句。意正确书写定义对象的语句。(3)尽管在一个类中可以包含多个构造函数,但是对尽管在一个类中可以包含多个构造函数,但是对于每一个对象来说,于每一个对象来说,建立对象时只执行其中一个构造建立对象时只执行其中一个构造函数函数,并非每个构造函数都被执行。,并非每个构造函数都被执行。构造函数中参数的值既可以通过实参传递,也可以指构造函数中参数的值既可以通过实参传递,也可以指定为某些默认值,即定为某些默认值,即如果用户不指定实参值,编译系如果用户不指定
18、实参值,编译系统就使形参取默认值统就使形参取默认值。例例9.4 将例将例9.3程序中的构造函数改用含默认值的参数,程序中的构造函数改用含默认值的参数,长、宽、高的默认值均为长、宽、高的默认值均为10。在例在例9.3程序的基础上改写如下:程序的基础上改写如下:9.1.6 使用默认参数的构造函数使用默认参数的构造函数#include using namespace std;class Boxpublic:Box(int h=10,int w=10,int len=10);/在声明构造函数时指定在声明构造函数时指定默认参数默认参数int volume();private:int height;int
19、 width;int length;Box Box(int h,int w,int len)/在定义函数时可以不指定默认参数在定义函数时可以不指定默认参数height=h;width=w;length=len;int Box volume()return(height*width*length);int main()Box box1;/没有给实参没有给实参 coutThe volume of box1 is box1.volume()endl;Box box2(15);/只给定一个实参只给定一个实参coutThe volume of box2 is box2.volume()endl;Box
20、box3(15,30);/只给定只给定2个实参个实参coutThe volume of box3 is box3.volume()endl;Box box4(15,30,20);/给定给定3个实参个实参coutThe volume of box4 is box4.volume()endl;return 0;析构函数析构函数(destructor)也是一个特殊的成员函数,它也是一个特殊的成员函数,它的作用与构造函数相反,它的的作用与构造函数相反,它的名字是类名的前面加一名字是类名的前面加一个个“”符号符号。在。在C+中中“”是位取反运算符,从是位取反运算符,从这点也可以想到:这点也可以想到:析构
21、函数是与构造函数作用相反析构函数是与构造函数作用相反的函数。的函数。当对象的生命期结束时,会自动执行析构函数当对象的生命期结束时,会自动执行析构函数。如果在一个函数中定义了一个对象如果在一个函数中定义了一个对象(它是自动局部它是自动局部对象对象),当这个函数被调用结束时,对象应该释放,当这个函数被调用结束时,对象应该释放,在对象释放前自动执行析构函数。在对象释放前自动执行析构函数。9.2 析构函数析构函数static局部对象局部对象,只在,只在main函数结束或调用函数结束或调用exit函数函数结束程序时,才调用结束程序时,才调用static局部对象的析构函数。局部对象的析构函数。全局对象全局
22、对象,则在程序的流程离开其作用域时,则在程序的流程离开其作用域时(如如main函数结束或调用函数结束或调用exit函数函数)时,调用该全局对象的析时,调用该全局对象的析构函数。构函数。如果用如果用new运算符运算符动态地建立了一个对象动态地建立了一个对象,当用,当用delete运算符释放该对象时,先调用该对象的析构函运算符释放该对象时,先调用该对象的析构函数。数。析构函数的作用:并不是删除对象,而是在撤销对象析构函数的作用:并不是删除对象,而是在撤销对象占用的内存之前完成一些清理工作,使这部分内存可占用的内存之前完成一些清理工作,使这部分内存可以被程序分配给新对象使用以被程序分配给新对象使用。
23、程序设计者事先设计好析构函数,只要对象的生命期程序设计者事先设计好析构函数,只要对象的生命期结束,程序就自动执行析构函数来完成这些工作。结束,程序就自动执行析构函数来完成这些工作。析构函数不返回任何值,没有函数类型,也没有函数析构函数不返回任何值,没有函数类型,也没有函数参数。参数。因此它不能被重载因此它不能被重载。一个类可以有多个构造函一个类可以有多个构造函数,但只能有一个析构函数。数,但只能有一个析构函数。析构函数的作用析构函数的作用并不仅限于释放资源并不仅限于释放资源方面,它还可以方面,它还可以输出有关的信息输出有关的信息。一般情况下,类的设计者应当在声明类的同时定义析一般情况下,类的设
24、计者应当在声明类的同时定义析构函数,以指定如何完成构函数,以指定如何完成“清理清理”的工作。的工作。如果用户没有定义析构函数,如果用户没有定义析构函数,C+编译系统会自动生编译系统会自动生成一个析构函数,但它只是徒有析构函数的名称和形成一个析构函数,但它只是徒有析构函数的名称和形式,式,实际上什么操作都不进行实际上什么操作都不进行。想让析构函数完成任。想让析构函数完成任何工作,都必须在定义的析构函数中指定。何工作,都必须在定义的析构函数中指定。例例9.5 包含构造函数和析构函数的包含构造函数和析构函数的C+程序。程序。#include#includeusing namespace std;cl
25、ass Student /声明声明Student类类public:student(int n,string nam,char s)/定义构造函数定义构造函数num=n;name=nam;sex=s;coutConstructor called.endl;/输出有关信息输出有关信息Student()/定义析构函数定义析构函数coutDestructor called.endl;/输出有关信息输出有关信息void display()/定义成员函数定义成员函数coutnum:numendl;coutname:nameendl;coutsex:sexendlendl;private:int num;c
26、har name10;char sex;int main()Student stud1(10010,Wang_li,f);/建立对象建立对象stud1stud1.display();/输出学生输出学生1的数据的数据 Student stud2(10011,Zhang_fun,m);/定义对象定义对象stud2stud2.display();/输出学生输出学生2的数据的数据return 0;程序运行结果如下:程序运行结果如下:Constructor called.(执行执行stud1的构造函数的构造函数)num:10010 (执行执行stud1的的display函数函数)name:Wang_li
27、sex:fConstructor called.(执行执行stud2的构造函数的构造函数)num:10011 (执行执行stud2的的display函数函数)name:Zhang_funsex:mDestructor called.(执行执行stud2的析构函数的析构函数)Destructor called.(执行执行stud1的析构函数的析构函数)在使用构造函数和析构函数时,需要特别注意对它们在使用构造函数和析构函数时,需要特别注意对它们的调用时间和调用顺序。的调用时间和调用顺序。在一般情况下,在一般情况下,调用析构函数的次序正好与调用构造调用析构函数的次序正好与调用构造函数的次序相反:函数
28、的次序相反:最先被调用的构造函数,其对应最先被调用的构造函数,其对应的的(同一对象中的同一对象中的)析构函数最后被调用,而最后被调析构函数最后被调用,而最后被调用的构造函数,其对应的析构函数最先被调用。如图用的构造函数,其对应的析构函数最先被调用。如图9.1示意。示意。9.3 调用构造函数和析构函数的顺序调用构造函数和析构函数的顺序图图9.1但是,并不是在任何情况下都是按这一原则处理的。但是,并不是在任何情况下都是按这一原则处理的。对象可以在不同的作用域中定义,可以有不同的存储对象可以在不同的作用域中定义,可以有不同的存储类别。这些会影响调用构造函数和析构函数的时机。类别。这些会影响调用构造函
29、数和析构函数的时机。下面归纳一下什么时候调用构造函数和析构函数:下面归纳一下什么时候调用构造函数和析构函数:(1)在全局范围中定义的对象在全局范围中定义的对象它的构造函数在文件中的所有函数它的构造函数在文件中的所有函数(包括包括main函数函数)执执行之前调用。当行之前调用。当main函数执行完毕或调用函数执行完毕或调用exit函数时函数时(此时程序终止此时程序终止),调用析构函数。,调用析构函数。(2)如果定义的是局部自动对象如果定义的是局部自动对象(例如在函数中定义对例如在函数中定义对象象),则在建立对象时调用其构造函数。如果函数被,则在建立对象时调用其构造函数。如果函数被多次调用,则在每
30、次建立对象时都要调用构造函数。多次调用,则在每次建立对象时都要调用构造函数。在函数调用结束、对象释放时先调用析构函数。在函数调用结束、对象释放时先调用析构函数。(3)如果在函数中定义静态如果在函数中定义静态(static)局部对象,则只在局部对象,则只在程序第一次调用此函数建立对象时调用构造函数一次,程序第一次调用此函数建立对象时调用构造函数一次,只在只在main函数结束或调用函数结束或调用exit函数结束程序时,才调函数结束程序时,才调用析构函数。用析构函数。数组也可以由对象组成数组也可以由对象组成(对象数组的每一个元素都是对象数组的每一个元素都是同类的对象同类的对象)。例如一个班有例如一个
31、班有50个学生,每个学生的属性包括姓名、个学生,每个学生的属性包括姓名、性别、年龄、成绩等。如果为每一个学生建立一个对性别、年龄、成绩等。如果为每一个学生建立一个对象,需要分别取象,需要分别取50个对象名。用程序处理很不方便。个对象名。用程序处理很不方便。这时可以定义一个这时可以定义一个“学生类学生类”对象数组,每一个数组对象数组,每一个数组元素是一个元素是一个“学生类学生类”对象。例如对象。例如 Student stud50;/假设已声明了假设已声明了Student类,定义类,定义stud数组,有数组,有50个元素个元素9.4 对象数组对象数组在建立数组时,同样要调用构造函数。如果有在建立数
32、组时,同样要调用构造函数。如果有50个元个元素,需要调用素,需要调用50次构造函数。次构造函数。如果构造函数有多个参数,则不能用在定义数组时直如果构造函数有多个参数,则不能用在定义数组时直接提供所有实参的方法,因为一个数组有多个元素,接提供所有实参的方法,因为一个数组有多个元素,对每个元素要提供多个实参,如果再考虑到构造函数对每个元素要提供多个实参,如果再考虑到构造函数有默认参数的情况,很容易造成实参与形参的对应关有默认参数的情况,很容易造成实参与形参的对应关系不清晰,出现歧义性。系不清晰,出现歧义性。如果构造函数有多个参数,在定义对象数组时应当怎如果构造函数有多个参数,在定义对象数组时应当怎
33、样实现初始化呢?样实现初始化呢?在花括号中分别写出构造函数并指定实参。如果构造在花括号中分别写出构造函数并指定实参。如果构造函数有函数有3个参数,分别代表学号、年龄、成绩。则可个参数,分别代表学号、年龄、成绩。则可以这样定义对象数组:以这样定义对象数组:Student Stud3=/定义对象数组定义对象数组Student(1001,18,87),/调用第调用第1个元素的构造函数,个元素的构造函数,为它提供为它提供3个实参个实参Student(1002,19,76),/调用第调用第2个元素的构造函数,个元素的构造函数,为它提供为它提供3个实参个实参Student(1003,18,72)/调用第调
34、用第3个元素的构造函数,个元素的构造函数,为它提供为它提供3个实参个实参;在建立对象数组时,分别调用构造函数,对每个元素在建立对象数组时,分别调用构造函数,对每个元素初始化。每一个元素的实参分别用括号包起来,对应初始化。每一个元素的实参分别用括号包起来,对应构造函数的一组形参,不会混淆。构造函数的一组形参,不会混淆。例例9.6 对象数组的使用方法。对象数组的使用方法。#include using namespace std;class Boxpublic:Box(int h=10,int w=12,int len=15):height(h),width(w),length(len)/声明有默认
35、参数的构造函数,用参数初始化表对数据成声明有默认参数的构造函数,用参数初始化表对数据成员初始化员初始化int volume();private:int height;int width;int length;int Box volume()return(height*width*length);int main()Box a3=/定义对象数组定义对象数组Box(10,12,15),/调用构造函数调用构造函数Box,提供第提供第1个元素的实个元素的实参参Box(15,18,20),/调用构造函数调用构造函数Box,提供第提供第2个元素的实个元素的实参参Box(16,20,26)/调用构造函数调用
36、构造函数Box,提供第提供第3个元素的实个元素的实参参;coutvolume of a0 is a0.volume()endl;coutvolume of a1 is a1.volume()endl;coutvolume of a2 is a2.volume()endl;运行结果如下:运行结果如下:volume of a0 is 1800volume of a1 is 5400volume of a2 is 8320在建立对象时,编译系统会为每一个对象分配一定的在建立对象时,编译系统会为每一个对象分配一定的存储空间,以存放其成员。存储空间,以存放其成员。对象空间的起始地址就是对象空间的起始地址
37、就是对象的指针对象的指针。可以定义一个指针变量,用来存放对象。可以定义一个指针变量,用来存放对象的指针。如果有一个类:的指针。如果有一个类:class Timepublic:int hour;int minute;int sec;void get_time();9.5 对象指针对象指针 9.5.1 指向对象的指针指向对象的指针void Time get_time()couthour:minute:sechour pt所指向的对象中的所指向的对象中的hour成员,即成员,即t1.hour(*pt).get_time()调用调用pt所指向的对象中的所指向的对象中的get_time函数,即函数,即t
38、1.get_timept-get_time()调用调用pt所指向的对象中的所指向的对象中的get_time函数,即函数,即t1.get_time对象有地址,存放对象初始地址的指针变量就是指向对象有地址,存放对象初始地址的指针变量就是指向对象的指针变量。对象中的成员也有地址,存放对象对象的指针变量。对象中的成员也有地址,存放对象成员地址的指针变量就是指向对象成员的指针变量。成员地址的指针变量就是指向对象成员的指针变量。9.5.2 指向对象成员的指针指向对象成员的指针1.指向对象数据成员的指针指向对象数据成员的指针定义指向对象数据成员的指针变量的方法和定义指向对象数据成员的指针变量的方法和定义指向
39、定义指向普通变量的指针变量方法相同普通变量的指针变量方法相同。例如。例如int*p1;/定义指向整型数据的指针变量定义指向整型数据的指针变量定义指向对象数据成员的指针变量的一般形式为定义指向对象数据成员的指针变量的一般形式为数据类型名数据类型名*指针变量名;指针变量名;如果如果Time类的数据成员类的数据成员hour为为公用的公用的整型数据,则整型数据,则可以在类外可以在类外通过指向对象数据成员的指针变量访问对通过指向对象数据成员的指针变量访问对象数据成员象数据成员hour。p1=&t1.hour;/将对象将对象t1的数据成员的数据成员hour的地址赋给的地址赋给p1,p1指向指向t1.hou
40、rcout*p1endl;/输出输出t1.hour的值的值例例9.7 有关对象指针的使用方法。有关对象指针的使用方法。#include using namespace std;class Timepublic:Time(int,int,int);int hour;int minute;int sec;void get_time();/声明公有成员函数声明公有成员函数;Time Time(int h,int m,int s)hour=h;minute=m;sec=s;void Time get_time()/定义公有成员函数定义公有成员函数couthour:minute:secendl;int
41、main()Time t1(10,13,56);/定义定义Time类对象类对象t1int*p1=&t1.hour;/定义指向整型数据的指针变量定义指向整型数据的指针变量p1,并使并使p1指指向向t1.hourcout*p1get_time();/调用调用p2所指向对象所指向对象(即即t1)的的get_time函数函数每个对象中的数据成员都分别占有存储空间,如果对每个对象中的数据成员都分别占有存储空间,如果对同一个类定义了同一个类定义了n个对象,则有个对象,则有n组同样大小的空间组同样大小的空间以存放以存放n个对象中的数据成员。但是,不同对象都调个对象中的数据成员。但是,不同对象都调用同一个函数
42、代码段。用同一个函数代码段。那么,当不同对象的成员函数引用数据成员时,怎么那么,当不同对象的成员函数引用数据成员时,怎么能保证引用的是所指定的对象的数据成员呢?能保证引用的是所指定的对象的数据成员呢?9.5.3 this 指针指针在每一个成员函数中都包含一个特殊的指针,这个指在每一个成员函数中都包含一个特殊的指针,这个指针的名字是固定的,称为针的名字是固定的,称为this。它是指向本类对象的指针,它的值是当前被调用的成它是指向本类对象的指针,它的值是当前被调用的成员函数所在的对象的起始地址员函数所在的对象的起始地址。例如,当调用成员函。例如,当调用成员函数数a.volume时,编译系统就把对象
43、时,编译系统就把对象a的起始地址赋给的起始地址赋给this指针,于是在成员函数引用数据成员时,就按照指针,于是在成员函数引用数据成员时,就按照this的指向找到对象的指向找到对象a的数据成员。例如的数据成员。例如volume函数函数要计算要计算height*width*length的值,实际上是执行:的值,实际上是执行:(this-height)*(this-width)*(this-length)由于当前由于当前this指向指向a,因此相当于执行:因此相当于执行:(a.height)*(a.width)*(a.length)这就计算出长方体这就计算出长方体a的体积。的体积。this指针是隐式
44、使用的,它是作为参数被传递给成员指针是隐式使用的,它是作为参数被传递给成员函数的函数的。本来,成员函数。本来,成员函数volume的定义如下:的定义如下:int Box volume()return(height*width*length);C+把它处理为把它处理为int Box volume(Box*this)return(this-height*this-width*this-length);即在成员函数的形参表列中增加一个即在成员函数的形参表列中增加一个this指针。在调指针。在调用该成员函数时,实际上是用以下方式调用的:用该成员函数时,实际上是用以下方式调用的:a.volume(&a)
45、;将对象将对象a的地址传给形参的地址传给形参this指针。然后按指针。然后按this的指向的指向去引用其他成员。去引用其他成员。需要说明:需要说明:这些都是编译系统自动实现的,编程序这些都是编译系统自动实现的,编程序者不必人为地在形参中增加者不必人为地在形参中增加this指针,也不必将对象指针,也不必将对象a的地址传给的地址传给this指针。指针。在需要时也可以显式地使用在需要时也可以显式地使用this指针指针。例如在。例如在Box类类的的volume函数中,下面两种表示方法都是合法的、函数中,下面两种表示方法都是合法的、相互等价的。相互等价的。return(height*width*leng
46、th);/隐含使用隐含使用this指针指针return(this-height*this-width*this-length);/显式使用显式使用this指针指针 可以用可以用*this表示被调用的成员函数所在的对象,表示被调用的成员函数所在的对象,*this就是就是this所指向的对象,即当前的对象。所指向的对象,即当前的对象。例如在例如在成员函数成员函数a.volume()的函数体中,如果出现的函数体中,如果出现*this,它它就是本对象就是本对象a。上面的上面的return语句也可写成语句也可写成 return(*this).height*(*this).width*(*this).le
47、ngth);注意注意*this两侧的括号不能省略,不能写成两侧的括号不能省略,不能写成*this.height。所谓所谓“调用对象调用对象a的成员函数的成员函数f”,实际上是在调用成实际上是在调用成员函数员函数f时使时使this指针指向对象指针指向对象a,从而访问对象从而访问对象a的成的成员。在使用员。在使用“调用对象调用对象a的成员函数的成员函数f”时,应当对它时,应当对它的含义有正确的理解。的含义有正确的理解。有时人们希望在需要用到对象时才建立对象,在不需有时人们希望在需要用到对象时才建立对象,在不需要用该对象时就撤销它,释放它所占的内存空间以供要用该对象时就撤销它,释放它所占的内存空间以
48、供别的数据使用。这样别的数据使用。这样可提高内存空间的利用率可提高内存空间的利用率。用用new运算符动态建立对象,用运算符动态建立对象,用delete运算符撤销对运算符撤销对象。象。如果已经定义了一个如果已经定义了一个Box类,可以用下面的方法动态类,可以用下面的方法动态地建立一个对象:地建立一个对象:9.7 对象的动态建立和释放对象的动态建立和释放new Box;编译系统开辟了一段内存空间,并在此内存空间中存编译系统开辟了一段内存空间,并在此内存空间中存放一个放一个Box类对象,同时调用该类的构造函数,以使类对象,同时调用该类的构造函数,以使该对象初始化该对象初始化(如果已对构造函数赋予此功
49、能的话如果已对构造函数赋予此功能的话)。但是此时用户还无法访问这个对象,因为这个对象既但是此时用户还无法访问这个对象,因为这个对象既没有对象名,用户也不知道它的地址。这种对象称为没有对象名,用户也不知道它的地址。这种对象称为无名对象无名对象,它确实是存在的,但它没有名字。,它确实是存在的,但它没有名字。用用new运算符动态地分配内存后,将返回一个指向新运算符动态地分配内存后,将返回一个指向新对象的指针的值,即所分配的内存空间的起始地址。对象的指针的值,即所分配的内存空间的起始地址。用户用户需要定义一个指向本类的对象的指针变量来存放需要定义一个指向本类的对象的指针变量来存放该地址该地址。如。如B
50、ox*pt;/定义一个指向定义一个指向Box类对象的指针变量类对象的指针变量ptpt=new Box;/在在pt中存放了新建对象的起始地址中存放了新建对象的起始地址在程序中就可以通过在程序中就可以通过pt访问这个新建的对象。如访问这个新建的对象。如 coutheight;/输出该对象的输出该对象的height成员成员coutvolume();/调用该对象的调用该对象的volume函数,计算并输出体积函数,计算并输出体积C+还还允许在执行允许在执行new时,对新建立的对象进行初始时,对新建立的对象进行初始化。化。如如(调带参数的构造函数)调带参数的构造函数)Box*pt=new Box(12,1
侵权处理QQ:3464097650--上传资料QQ:3464097650
【声明】本站为“文档C2C交易模式”,即用户上传的文档直接卖给(下载)用户,本站只是网络空间服务平台,本站所有原创文档下载所得归上传人所有,如您发现上传作品侵犯了您的版权,请立刻联系我们并提供证据,我们将在3个工作日内予以改正。