1、2023-11-121 1第第3章章 类和对象类和对象本章学习要点本章学习要点类和对象的概念及其关系类的声明和对象的定义及使用构造函数与析构函数的作用及使用对象数组 对象与指针 对象与const对象的动态创建和释放对象的复制和赋值 对象的移动向函数传递对象字面值常量类图书馆图书借阅管理系统中类的声明和对象的定义2023-11-122 2第第3章章 类和对象类和对象本章学习目标本章学习目标正确理解类、对象及封装的概念熟练掌握类的声明和对象的定义及使用理解构造函数与析构函数的作用及使用掌握对象与指针及const的特点及使用理解并掌握对象的复制和赋值,对象的移动掌握对象的动态创建和释放理解对象作为函
2、数的参数传递的实质掌握基本的基于对象的程序设计与实现2023-11-123 33.1 类的声明和对象的定义类的声明和对象的定义v本节主要是关于类和对象的概念、类的声本节主要是关于类和对象的概念、类的声明格式、对象的定义方法明格式、对象的定义方法 2023-11-124 43.1.1 类和对象的概念及关系类和对象的概念及关系v对象对象 封装了数据及在这些数据之上的操作的封装了数据及在这些数据之上的操作的封装体封装体 v类类 对具有相同属性和操作的一组对象的抽对具有相同属性和操作的一组对象的抽象描述象描述 v类和对象的关系类和对象的关系 类是对象的抽象;对象是对类的实例化类是对象的抽象;对象是对类
3、的实例化2023-11-125 5类和对象的关系类和对象的关系3.1.1 类和对象的概念及关系类和对象的概念及关系2023-11-126 63.1.2 类类的声明的声明class 类名类名public:公用成员公用成员 protected:受保护成员受保护成员 private:私有成员私有成员 ;声明类的一般形式:声明类的一般形式:声明类的关键字声明类的关键字合法标识符合法标识符公用成员限定符公用成员限定符受保护成员限定符受保护成员限定符私有成员限定符私有成员限定符类结束要加分号类结束要加分号2023-11-127 73.1.2 类类的声明的声明【例例3-1】声明一个学生类,要求包括学生的学号
4、、声明一个学生类,要求包括学生的学号、姓名、性别等信息,并且能够显示学生的信息。姓名、性别等信息,并且能够显示学生的信息。2023-11-128 83.1.2 类类的声明的声明class Student /声明学生类声明学生类Studentpublic:/以下部分为公用成员函数以下部分为公用成员函数 void setInfo()cout stuNo stuName stuSex stuAge;void show()cout No.:stuNo endl;cout Name:stuName endl;cout Sex:stuSex endl;cout Age:stuAge endl;privat
5、e:/以下部分为学生类以下部分为学生类Student的私有数据成员的私有数据成员string stuNo;/学号学号 string stuName;/姓名姓名 string stuSex=男男;/性别性别 int stuAge=0;/年龄年龄;/类声明结束,此处必须有分号类声明结束,此处必须有分号2023-11-129 93.1.2 类类的声明的声明1.类的成员分为数据成员和成员函数类的成员分为数据成员和成员函数2.成员的可访问性可分为三类:私有的(成员的可访问性可分为三类:私有的(private)、受保)、受保护的(护的(protected)、公用的()、公用的(public)3.由访问限定
6、符限定它后面的成员的访问属性,直到出现由访问限定符限定它后面的成员的访问属性,直到出现下一个访问限定符或者类的结束为止下一个访问限定符或者类的结束为止4.在声明类时,这三种访问属性的成员的声明次序是任意在声明类时,这三种访问属性的成员的声明次序是任意的,并且在一个类的声明中不一定这三种访问属性全部都的,并且在一个类的声明中不一定这三种访问属性全部都出现,可能只出现两种或一种。出现,可能只出现两种或一种。5.某个成员访问限定符在一个类的声明中也可以出现多次。某个成员访问限定符在一个类的声明中也可以出现多次。2023-11-1210103.1.3 对象的定义对象的定义v声明类之后,再定义对象声明类
7、之后,再定义对象 如:如:Student zhang,wang;或:或:class Student zhang,wang;v在声明类的同时定义对象在声明类的同时定义对象 如:如:class Student zhang,wang;v不出现类名,直接定义对象不出现类名,直接定义对象 如:如:class zhang,wang;声明类时系统并不分配内存单元,而定义对象声明类时系统并不分配内存单元,而定义对象时系统会给每个对象分配内存单元时系统会给每个对象分配内存单元2023-11-1211113.2.1 成员函数的性质成员函数的性质v不属于任何类的函数称为不属于任何类的函数称为普通函数普通函数。v成员
8、函数隶属于某个类成员函数隶属于某个类v成员函数与普通函数的区别成员函数与普通函数的区别 成员函数是属于某个类的成员函数是属于某个类的,是类的一个成员是类的一个成员 成员函数可以指定访问属性成员函数可以指定访问属性 成员函数可以访问本类的任何成员,而普通函成员函数可以访问本类的任何成员,而普通函数只能访问对象的公用成员数只能访问对象的公用成员v对于成员函数,对于成员函数,一般是将需要被类外调用一般是将需要被类外调用的声明为公用的,不需要被类外调用的声的声明为公用的,不需要被类外调用的声明为私有的。明为私有的。2023-11-121212【例例3-4】将将Student类的成员函数改为在类外定义类
9、的成员函数改为在类外定义的形式。的形式。3.2.2 在类外定义成员函数在类外定义成员函数2023-11-121313class Student/声明声明Student类类型类类型public:/以下部分为公用成员函数以下部分为公用成员函数 void setInfo();void show();private:/以下部分是私有数据成员以下部分是私有数据成员 int stuNo;/学号学号 char stuName20;/姓名姓名 string stuSex=男男;/性别性别 int stuAge=0;/年龄年龄;/类声明结束类声明结束/在类的声明之外定义成员函数在类的声明之外定义成员函数/在类的
10、声明之外定义学生类在类的声明之外定义学生类Student的的setInfo成员函数成员函数void Student:setInfo()cout stuNo stuName stuSex stuAge;2023-11-121414class Student/声明声明Student类类型类类型public:/以下部分为公用成员函数以下部分为公用成员函数 void setInfo();void show();private:/以下部分是私有数据成员以下部分是私有数据成员 int stuNo;/学号学号 char stuName20;/姓名姓名 string stuSex=男男;/性别性别 int s
11、tuAge=0;/年龄年龄;/类声明结束类声明结束/在类的声明之外定义成员函数在类的声明之外定义成员函数 void Student:show()coutNo.:stuNoendl;coutName:stuNameendl;coutSex:stuSexendl;cout Age:stuAge endl;2023-11-1215153.2.2 在类外定义成员函数在类外定义成员函数2023-11-1216163.2.3 inline成员函数成员函数vinline函数函数 在编译时将被调用函数的代码直接嵌入在编译时将被调用函数的代码直接嵌入到调用函数处到调用函数处 vinline成员函数就是将类中的成
12、员函数成员函数就是将类中的成员函数声明为内置的声明为内置的 当类中的成员函数是在类内定义时,默当类中的成员函数是在类内定义时,默认该成员函数是认该成员函数是inline成员函数成员函数 如果成员函数定义在类的外部,则在成如果成员函数定义在类的外部,则在成员函数声明或定义前必须要有员函数声明或定义前必须要有inline关键关键字字 2023-11-1217173.2.4 成员函数的存储方式成员函数的存储方式v实例化对象时需要分配内存空间,数据和函实例化对象时需要分配内存空间,数据和函数都需要存储空间数都需要存储空间v同一个类的不同对象的数据是不一样的,因同一个类的不同对象的数据是不一样的,因此要
13、为每个对象的数据成员分配内存单元此要为每个对象的数据成员分配内存单元v同一个类的不同对象的函数是一样的,因此同一个类的不同对象的函数是一样的,因此将同类的所有对象的函数放在一个公共的区将同类的所有对象的函数放在一个公共的区域。域。2023-11-1218183.2.4 成员函数的存储方式成员函数的存储方式【例例3-5】类的对象占用内存空间实验。类的对象占用内存空间实验。#include using namespace std;class Testpublic:void show()coutchar in Test is:cendl;private:char c;int main()Test t
14、est;coutSize of Test is sizeof(test)endl;return 0;2023-11-1219193.2.4 成员函数的存储方式成员函数的存储方式【例【例3-6】相同类的不同对象执行相同成员函数输出相同类的不同对象执行相同成员函数输出不同结果。不同结果。#include using namespace std;class Testpublic:void set(char ch)c=ch;void show()coutchar in Test is:cc=ch;void show(Test*this)coutchar in Test is:c”运算符方便直观的进行,
15、运算符方便直观的进行,“-”称为指向运算符。称为指向运算符。例如:例如:Test test1;test1.set(a);Test*pTest=&test1;test1.show();pTest-show();此处两个语句的执行结此处两个语句的执行结果完全相同,只是采用果完全相同,只是采用了不同的访问对象成员了不同的访问对象成员的形式而已的形式而已。2023-11-1224243.3.3 通过引用访问成员通过引用访问成员v对象的引用和普通变量的引用在本质上是一样对象的引用和普通变量的引用在本质上是一样的的 v通过对象的引用访问对象成员和通过对象访问通过对象的引用访问对象成员和通过对象访问成员形式
16、上是一样的成员形式上是一样的例如:例如:Test test1;test1.set(a);Test&refTest=test1;test1.show();refTest.show();此处两个语句的执行此处两个语句的执行结果完全相同,只是结果完全相同,只是采用了不同的访问对采用了不同的访问对象成员的形式而已象成员的形式而已。class Student/声明声明Student类类型类类型public:/以下部分为公用成员函数以下部分为公用成员函数 void setInfo();void show();private:/以下部分是私有数据成员以下部分是私有数据成员 int stuNo;/学号学号 c
17、har stuName20;/姓名姓名 string stuSex=男男;/性别性别 int stuAge=0;/年龄年龄;/类声明结束类声明结束/在类的声明之外定义成员函数在类的声明之外定义成员函数 void Student:show()coutNo.:stuNoendl;coutName:stuNameendl;coutSex:stuSexendl;cout Age:stuAge endl;2023-11-122525int main()Student stud;stud.show();return 0;2023-11-122626class Student/声明声明Student类类型类
18、类型public:/以下部分为公用成员函数以下部分为公用成员函数 void setInfo();void show();private:/以下部分是私有数据成员以下部分是私有数据成员 int stuNo;/学号学号 char stuName20;/姓名姓名 string stuSex=男男;/性别性别 int stuAge=0;/年龄年龄;/类声明结束类声明结束/在类的声明之外定义成员函数在类的声明之外定义成员函数 void Student:Show()coutNo.:stuNoendl;coutName:stuNameendl;coutSex:stuSexendl;void Student:
19、setInfo()cout stuNo stuName stuSex stuAge;int main()Student stud;stud.setInfo();stud.show();return 0;2023-11-1227273.4.1 构造函数构造函数1、构造函数的任务、构造函数的任务 创建对象时对对象的数据成员初始化创建对象时对对象的数据成员初始化【例【例3-9】构造函数举例构造函数举例2023-11-122828class SalesData /声明图书交易记录类SalesDatapublic:/定义SalesData类的构造函数,构造函数名与类名相同 SalesData()/利用构
20、造函数对数据成员赋初值 bookNo=null;unitsSold=0;revenue=0.0;cout SalesDatas constructor is executed!endl;private:string bookNo;/书号书号 int unitsSold;/销售出的册数销售出的册数 double revenue;/总销售金额总销售金额;/类声明结束类声明结束2023-11-1229293.4.1 构造函数构造函数v 构造函数的特点构造函数的特点(1)构造函数是类的一个特殊的成员函数,构造函数名与)构造函数是类的一个特殊的成员函数,构造函数名与类名相同,且没有返回值类名相同,且没有
21、返回值(2)构造函数可以被重载,一个类可以包含多个构造函数,)构造函数可以被重载,一个类可以包含多个构造函数,不同的构造函数之间在形参数量或形参类型上有所不同。不同的构造函数之间在形参数量或形参类型上有所不同。稍后介绍。稍后介绍。(3)构造函数不需要用户调用,由系统在创建对象时自动)构造函数不需要用户调用,由系统在创建对象时自动调用的调用的(4)构造函数不能声明为)构造函数不能声明为const的。的。(5)构造函数内容一般是初始化数据语句,但也可以是其)构造函数内容一般是初始化数据语句,但也可以是其他的语句他的语句(6)创建对象时肯定会执行一个构造函数)创建对象时肯定会执行一个构造函数2023
22、-11-1230303.4.1 构造函数构造函数v默认构造函数默认构造函数 如果用户自己没有定义构造函数,编译器会隐式地如果用户自己没有定义构造函数,编译器会隐式地提供一个构造函数,称之为合成的默认构造函数,提供一个构造函数,称之为合成的默认构造函数,该构造函数的形参表和函数体皆为空,它按如下规该构造函数的形参表和函数体皆为空,它按如下规则初始化类的数据成员:则初始化类的数据成员:v如果存在类内的初始值,用它来初始化成员,这是如果存在类内的初始值,用它来初始化成员,这是C+11标准中新增的类内初始化。标准中新增的类内初始化。v否则,默认初始化该成员,其初始化规则与普通变否则,默认初始化该成员,
23、其初始化规则与普通变量的默认初始化规则相同。量的默认初始化规则相同。2023-11-1231313.4.1 构造函数构造函数v默认构造函数默认构造函数 合成的默认构造函数只适合非常简单的类,对于大合成的默认构造函数只适合非常简单的类,对于大多数普通的类来说,必须定义它自己的默认构造函多数普通的类来说,必须定义它自己的默认构造函数(该构造函数或者形参表为空,如例数(该构造函数或者形参表为空,如例3-9中的构中的构造函数,或者形参不为空但全部参数都有默认实参造函数,或者形参不为空但全部参数都有默认实参)。)。2023-11-1232323.4.1 构造函数构造函数2、定义自己的构造函数、定义自己的
24、构造函数 class SalesData /声明图书交易记录类SalesDatapublic:SalesData()=default;/无参的默认构造函数 /带1个const string&参数的构造函数 SalesData(const string&No):bookNo(No)/带1个istream&参数的构造函数 SalesData(istream&input);/带3个参数(书号、销售册数、销售价格)的构造函数 SalesData(const string&No,int n,double price):bookNo(No),unitsSold(n),revenue(n*price);/类
25、声明结束类声明结束2023-11-1233333.4.1 构造函数构造函数2、定义自己的构造函数、定义自己的构造函数 int main()SalesData book1;book1.show();cout endl;SalesData book2(1002);book2.show();cout endl;SalesData book3(1003,2,35.0);book3.show();cout bookNo unitsSold price;revenue=unitsSold*price;(此处省略的代码同例3-10)private:string bookNo;/书号 int unitsSol
26、d;/销售出的册数 double revenue;/总销售金额;2023-11-1243431.委托构造函数的初始化列表中有且仅有一个对目标构造函数的调用。即,如果委托构造函数的初始化列表中有一个对目标构造函数的调用,则该初始化列表中就不能再有其它东西(即不允许再有其它基类或数据成员的初始化)。2.目标构造函数还可以再委托给另一个构造函数,但不要形成委托环。如,构造函数C1委托给另一个构造函数C2,而C2又委托给C1,这样的代码通常会导致编译错误。2023-11-1244443.委托构造函数的函数体中的语句在目标构造函数完全执行后才被执行。4.对象的生命期从任意一个构造函数执行完毕开始(对于委
27、托构造的情况,就是最终的目标构造函数执行完毕时),这意味着从委托构造函数体中抛出异常将导致析构函数的自动执行。2023-11-1245453.4.2 析构函数析构函数v析构函数的析构函数的作用作用:在系统释放对象之前进行清理工作。在系统释放对象之前进行清理工作。v析构函数的特点析构函数的特点 析构函数的函数名是固定的,由析构函数的函数名是固定的,由“”+“类名类名”组成组成 析构函数没有返回值析构函数没有返回值 析构函数没有参数,因此析构函数无法重载。一个析构函数没有参数,因此析构函数无法重载。一个类有且仅有一个析构函数类有且仅有一个析构函数 如果没有自己写出析构函数,系统会自动生成一个如果没
28、有自己写出析构函数,系统会自动生成一个析构函数。合成析构函数的函数体为空。析构函数。合成析构函数的函数体为空。2023-11-1246463.4.2 析构函数析构函数v析构函数的特点析构函数的特点 析构函数在对象生命周期结束时由系统析构函数在对象生命周期结束时由系统自动调用自动调用2023-11-1247473.4.2 析构函数析构函数v如同构造函数有一个初始化部分和一个函数体,如同构造函数有一个初始化部分和一个函数体,析构函数也有一个函数体和一个析构部分。在一析构函数也有一个函数体和一个析构部分。在一个构造函数中,成员的初始化是在函数体执行之个构造函数中,成员的初始化是在函数体执行之前完成的
29、,且按照它们在类中出现的顺序进行初前完成的,且按照它们在类中出现的顺序进行初始化。在一个析构函数中,首先执行函数体,然始化。在一个析构函数中,首先执行函数体,然后析构成员。成员按初始化顺序的逆序析构。后析构成员。成员按初始化顺序的逆序析构。v在析构函数的函数体内,可以书写在对象最后一在析构函数的函数体内,可以书写在对象最后一次使用之后类设计者希望执行的任何收尾工作。次使用之后类设计者希望执行的任何收尾工作。通常,析构函数的函数体完成释放对象在生存期通常,析构函数的函数体完成释放对象在生存期分配的所有资源。分配的所有资源。2023-11-1248483.4.2 析构函数析构函数v析构部分是隐式的
30、。成员析构时发生什么完全依析构部分是隐式的。成员析构时发生什么完全依赖于成员的类型。析构类类型的成员系统会自动赖于成员的类型。析构类类型的成员系统会自动执行成员对象自己的析构函数。内置类型没有析执行成员对象自己的析构函数。内置类型没有析构函数,因此析构内置类型成员什么也不需要做构函数,因此析构内置类型成员什么也不需要做。注意:隐式析构一个内置指针类型的成员不会。注意:隐式析构一个内置指针类型的成员不会delete它所指向的对象。它所指向的对象。认识到认识到析构函数体自身并不直接析构成员析构函数体自身并不直接析构成员是非常重是非常重要的。成员是在析构函数体之后隐含的析构阶段中要的。成员是在析构函
31、数体之后隐含的析构阶段中被析构的。在整个对象被析构过程中,析构函数体被析构的。在整个对象被析构过程中,析构函数体是作为成员析构步骤之外的另一部分进行的。是作为成员析构步骤之外的另一部分进行的。2023-11-1249493.4.2 析构函数析构函数【例3-11】简化版的SalesData类。class SalesData /声明图书交易记录类SalesDatapublic:/受委托的构造函数,也称为目标构造函数 SalesData(const string&No,int n,double price):bookNo(No),unitsSold(n),revenue(n*price)/其余构造函
32、数全部委托给另一个构造函数 SalesData():SalesData(null,0,0)SalesData(const string&No):SalesData(No,0,0)SalesData(istream&input):SalesData()double price;input bookNo unitsSold price;revenue=unitsSold*price;2023-11-1250503.4.2 析构函数析构函数【例3-11】简化版的SalesData类。class SalesData /声明图书交易记录类SalesDatapublic:SalesData()/对象被销毁
33、前输出图书基本销售记录:书号、销售册数、总销售金额、销售均价 cout bookNo=bookNo;cout ,unitsSold=unitsSold;cout ,revenue=revenue;if(unitsSold!=0)cout ,avgPrice=avgPrice();cout=0);len=size;str=new charlen+1;assert(str!=nullptr);for(unsigned int i=0;i len;i+)stri=0;2023-11-1260603.4.3 构造函数和析构函数调用次序构造函数和析构函数调用次序String:String(char c)
34、/带一个字符形参的构造函数,传递一个字符带一个字符形参的构造函数,传递一个字符 len=1;str=new charlen+1;assert(str!=nullptr);str0=c;str1=0;String:String(const char*src)/带一个字符指针的构造函数,传递一个字符串带一个字符指针的构造函数,传递一个字符串 len=strlen(src);str=new charlen+1;assert(str!=nullptr);strcpy(str,src);2023-11-1261613.4.3 构造函数和析构函数调用次序构造函数和析构函数调用次序String:String
35、()/析构函数析构函数 delete str;str=nullptr;/动态释放指针动态释放指针str所指向的内存空间所指向的内存空间2023-11-1262623.5 对象数组对象数组v对象数组和普通的数组没有本质的区别,对象数组和普通的数组没有本质的区别,只不过普通的数组的元素是简单变量,而只不过普通的数组的元素是简单变量,而对象数组的元素是对象而已对象数组的元素是对象而已 v对象数组在实际中主要用于系统需要一个对象数组在实际中主要用于系统需要一个类的多个对象的情况类的多个对象的情况 v在建立数组时,系统会自动调用每一个对在建立数组时,系统会自动调用每一个对象元素的构造函数象元素的构造函数
36、2023-11-126363【例例3-13】创建含有创建含有3个长方体元素的长方体数组,个长方体元素的长方体数组,并显示长方体构造函数的调用情况。并显示长方体构造函数的调用情况。教材教材P993.5 对象数组对象数组2023-11-1264643.4.2 析构函数析构函数#include using namespace std;class Boxpublic:Box()Box(float L,float w,float h)Box()float volume()return length*width*height;private:float length,width,height;int ma
37、in()Box boxs3;/创建含有三个元素的对象数组boxs return 0;2023-11-1265653.4.2 析构函数析构函数int main()Box boxs3;/创建含有三个元素的对象数组boxs return 0;程序执行后运行的结果如下:程序执行后运行的结果如下:Box(1,1,1)is constructed!Box(1,1,1)is constructed!Box(1,1,1)is constructed!Box(1,1,1)is destructed!Box(1,1,1)is destructed!Box(1,1,1)is destructed!2023-11-1
38、266663.5 对象数组对象数组v对象数组在建立时还可以给出实参以对象数组在建立时还可以给出实参以实现对数组元素进行不同的初始化实现对数组元素进行不同的初始化 如果如果Box类有只有一个参数的构造函数,类有只有一个参数的构造函数,可以使用如下形式的数组初始化形式:可以使用如下形式的数组初始化形式:Box boxs3=10,20,30;Box boxs3=10,20,30;如果对象的构造函数需要多个参数,则如果对象的构造函数需要多个参数,则在初始化的花括号里要分别写明构造函在初始化的花括号里要分别写明构造函数,并指定实参数,并指定实参 v对象数组初始化参见对象数组初始化参见【例例3-14】20
39、23-11-126767【例例3-14】定义对象数组并初始化,观察对象数组定义对象数组并初始化,观察对象数组建立的情况。建立的情况。教材教材P1013.5 对象数组对象数组2023-11-1268683.4.2 析构函数析构函数#include using namespace std;class Boxpublic:Box()Box(float L,float w,float h)Box()float volume()return length*width*height;private:float length,width,height;2023-11-1269693.4.2 析构函数析构函数
40、int main()/创建含有三个元素的对象数组boxes Box boxs3=Box(1,3,5),Box(2,4,6),Box(3,6,9);/pbegin指向boxs数组的首元素,pend指向boxs数组尾元素的下一位置 Box*pbegin=begin(boxs),*pend=end(boxs);/遍历对象数组boxs,计算并输出每个Box类对象的体积 while(pbegin!=pend)cout volume=volume()”调用对象的公用成员函数调用对象的公用成员函数 通过通过“*”运算符得到对象,然后再使用运算符得到对象,然后再使用“.”运算符调用对象的公用成员函数运算符调用
41、对象的公用成员函数 2023-11-1273733.6.2 指向对象成员的指针指向对象成员的指针v指向对象数据成员的指针指向对象数据成员的指针 指向对象数据成员的指针和普通的指针是完全相同指向对象数据成员的指针和普通的指针是完全相同的,声明指针变量的格式如下:的,声明指针变量的格式如下:数据类型名数据类型名*指针变量名指针变量名;而使指针指向对象的公用数据成员使用如下语句:而使指针指向对象的公用数据成员使用如下语句:指针变量指针变量=&对象名对象名.数据成员名数据成员名;设类设类A有公用整型数据成员有公用整型数据成员data,定义了一个整型指,定义了一个整型指针针p和和A类对象类对象a,则,则
42、 p=&a.data;/使整型指针使整型指针p指向对象指向对象a的数据成员的数据成员data cout*pendl;2023-11-1274743.6.2 指向对象成员的指针指向对象成员的指针v指向对象成员函数的指针指向对象成员函数的指针 指向对象成员函数的指针需要在指针名指向对象成员函数的指针需要在指针名前面加上所属的类名及域运算符前面加上所属的类名及域运算符“:”指向对象成员函数的指针不但要匹配将指向对象成员函数的指针不但要匹配将要指向函数的参数类型、个数和返回值要指向函数的参数类型、个数和返回值类型,还要匹配将要指向函数所属的类类型,还要匹配将要指向函数所属的类 2023-11-1275
43、753.6.2 指向对象成员的指针指向对象成员的指针v指向普通函数的指针变量定义如下:指向普通函数的指针变量定义如下:v 返回值类型返回值类型(*指针名指针名)(参数表参数表);v而指向成员函数的指针变量定义如下:而指向成员函数的指针变量定义如下:v 返回值类型返回值类型(类名类名:*指针名指针名)(参数表参数表);v使用指向成员函数的指针指向一个公用成员函数时使用指向成员函数的指针指向一个公用成员函数时如下:如下:v 指针名指针名=&类名类名:成员函数名成员函数名;v使用指向成员函数的指针调用对象的成员函数时如使用指向成员函数的指针调用对象的成员函数时如下:下:v (对象名对象名.*指针名指
44、针名)(实参表实参表);2023-11-1276763.6.2 指向对象成员的指针指向对象成员的指针【例例3-15】使用指向对象成员函数的指针调用对象的使用指向对象成员函数的指针调用对象的成员函数成员函数.int main()Box box(2,2,2);/创建创建Box的对象的对象box /定义指向定义指向Box类的成员函数的指针类的成员函数的指针p float(Box:*p)();/给指针给指针p赋值,使其指向赋值,使其指向Box类的成员函数类的成员函数Volume p=&Box:volume;/调用指针调用指针p指向的函数指向的函数 cout“The volume of box is(b
45、ox.*p)()height)*(this-width)*(this-length)vthis指针是指向本类对象的指针,它的指向指针是指向本类对象的指针,它的指向是被调用成员函数所在的对象是被调用成员函数所在的对象vthis指针是由系统通过参数隐式传递给成员指针是由系统通过参数隐式传递给成员函数的函数的2023-11-127979this指针的处理过程指针的处理过程源程序编译后调用时float Box:volume()return length*width*height;float Box:volume(Box*this)return this-length*this-width*this-h
46、eight;box1.volume(&box1);2023-11-1280803.7 对象与对象与constv在程序设计的过程中需要考虑的一个非常重要在程序设计的过程中需要考虑的一个非常重要的因素就是数据的安全性的因素就是数据的安全性v在程序中,不同的模块之间经常出现需要共享在程序中,不同的模块之间经常出现需要共享数据的情况,此时数据的安全性降低数据的情况,此时数据的安全性降低v在程序中既要让数据在一定范围内共享在程序中既要让数据在一定范围内共享,又要又要保证数据的安全,这时可以使用保证数据的安全,这时可以使用const,把对,把对象或对象相关成员定义成常量象或对象相关成员定义成常量v本节主要
47、从本节主要从常对象常对象、常对象成员常对象成员、指向对象的指向对象的常指针常指针、指向常对象的指针指向常对象的指针和和对象的常引用对象的常引用五五个方面进行说明个方面进行说明2023-11-1281813.7.1 常对象常对象v常对象中的所有成员的值都不能被修改常对象中的所有成员的值都不能被修改v常对象中数据成员为常变量且必须有初值常对象中数据成员为常变量且必须有初值v声明常对象的一般形式声明常对象的一般形式 const 类名类名 对象名对象名(实参表实参表);类名类名 const 对象名对象名(实参表实参表);v如果一个对象被声明为常对象,则不能调如果一个对象被声明为常对象,则不能调用该对象
48、的非用该对象的非const型的成员函数(构造函型的成员函数(构造函数和析构函数除外)数和析构函数除外)v在成员函数声明的后面加上在成员函数声明的后面加上const即可访问即可访问2023-11-1282823.7.1 常对象常对象v如何将一个成员函数声明成如何将一个成员函数声明成const型成员函型成员函数呢?数呢?v其实很简单,只需要在成员函数声明的后其实很简单,只需要在成员函数声明的后面加上面加上const即可。即可。float volume()const;特别提醒:特别提醒:有时在编程时有要求,一定要修改常对象中的某个数据成员的有时在编程时有要求,一定要修改常对象中的某个数据成员的值,值
49、,ANSI C+ANSI C+考虑到实际编程时的需要,对此作了特殊的处理考虑到实际编程时的需要,对此作了特殊的处理,将该数据成员声明为,将该数据成员声明为mutablemutable,如:,如:mutable int count;mutable int count;把把countcount声明为可变的数据成员,这样就可以用声明为声明为可变的数据成员,这样就可以用声明为constconst的成员函数来修改它的值。的成员函数来修改它的值。2023-11-1283833.7.2 常对象成员常对象成员v常数据成员常数据成员 常数据成员的声明和作用与普通的常变量类似常数据成员的声明和作用与普通的常变量类
50、似 在程序运行过程中常数据成员的值不能修改在程序运行过程中常数据成员的值不能修改 常变量在声明的同时必须初始化常变量在声明的同时必须初始化 注意常数据成员在初始化时必须使用构造函数注意常数据成员在初始化时必须使用构造函数的参数初始化表的参数初始化表2023-11-1284843.7.2 常对象成员常对象成员 例:例:若若Box类中的数据成员类中的数据成员length声明为常数声明为常数据成员,则如下的构造函数:据成员,则如下的构造函数:Box:Box(float L,float w,float h)length=L;width=w;height=h;不能使用这种形不能使用这种形式进行初始化式进