第九讲共用数据的保护与对象的动态处理Word文件下载.docx
《第九讲共用数据的保护与对象的动态处理Word文件下载.docx》由会员分享,可在线阅读,更多相关《第九讲共用数据的保护与对象的动态处理Word文件下载.docx(7页珍藏版)》请在冰豆网上搜索。
类名const对象名[(实参表列)];
const类名对象名[(实参表列)];
二者等价。
说明:
1、在定义对象时指定对象为常对象。
常对象中的数据成员为常变量且必须要有初值,这样,在所有的场合中,对象tl中的所有数据成员的值都不能被修改。
凡希望保证数据成员不被改变的对象,可以声明为常对象。
2、如果一个对象被声明为常对象,则不能调用该对象的普通(非const型)的成员函数(除了由系统自动调用的隐式的构造函数和析构函数)。
3、为了防止普通函数(非const型)会修改常对象中数据成员的值。
在函数中并没有修改常对象中数据成员的值也不允许调用,因为不能仅依靠编程者的细心来保证程序不出错,编译系统充分考虑到可能出现的情况,对不安全的因素予以拦截。
4、编译系统不能进入函数去检查它的代码,看它是否修改了常对象中数据成员的值。
实际上,函数的定义与函数的声明可能不在同一个源程序文件中。
而编译则是以一个源程序文件为单位的,无法测出两个源程序文件之间是否有矛盾。
如果有错,只有在连接或运行阶段才能发现。
这就给调试程序带来不便。
5、现在,编译系统只检查函数的声明,只要发现调用了常对象的成员函数,而且该函数未被声明为const,就报错,提请编程者注意。
6、有时在编程时有要求,一定要修改常对象中的某个数据成员的值(例如类中有一个用于计数的变量count,其值应当能不断变化),ANSI考虑到实际编程时的需要,对作了特殊的处理,对该数据成员声明为mutable。
如:
mutableincount;
把count声明为可变的数据成员,这样就可以用声明为const的成员函数来修改它的值。
2、常对象成员(类)
可以在声明类时将成员声明为const,即声明常数据成员和常成员函数。
①常数据成员
作用和用法与一般常变量相似,用关键字const来声明常数据成员。
常数据成员的值是不能改变的。
有一点要注意:
只能通过构造函数的参数初始化表对常数据成员进行初始化。
如在类体中声明了常数据成员hour:
constinthour;
//声明hour为常数据成员
不能采用在构造函数中对常数据成员赋初值的方法,下面的用法是非法的:
Time:
:
Time(inth)
{hour=h;
}//非法,因为常数据成员是不能被赋值的。
如果在类外定义构造函数,应写成以下形式:
Time(inth):
hour(h){}//通过参数初始化表对常数据成员hour初始化在类体中声明了某一个数据成员为常数据成员后,该类的所有对象中的该数据成员的值都是不能改变的,但不同对象中的该数据成员的值可以是不同的(在定义对象时给出)。
②常成员函数
常成员函数的定义:
类型函数名const
1、如果将成员函数声明为常成员函数,则只能引用本类中的数据成员,而不能修改它们,例如只用于输出数据等。
一般的成员函数可以引用本类中的非const数据成员,也可以修改它们。
2、在声明函数和定义函数时都要有const关键字,在调用时不必加const。
常成员函数可以引用const数据成员,也可以引用非const的数据成员。
3、const数据成员可以被const成员函数引用,也可以被非const的成员函数引用。
具体情况可以用下表1表示。
怎样利用常成员函数呢?
(1)如果在一个类中,有些数据成员的值允许改变,另一些数据成员的值不允许改变,则可以将一部分数据成员声明为const,以保证其值不被改变。
非const成员函数可以引用这些数据成员的值,并修改非const数据成员的值。
(2)如果要求所有的数据成员的值都不允许改变,则可以将所有的数据成员声明为const,或将对象声明为const(常对象),然后用coast成员函数引用数据成员,这样起到“双保险”的作用,切实保证了数据成员不被修改。
(3)如果已定义了一个常对象,只能调用其中的const成员函数,而不能调用非const成员函数(不论这些函数是否会修改对象中的数据)。
这是为了保证数据的安全。
如果需要访问对象中的数据成员,可将常对象中所有成员函数都声明为const成员函数,并确保在函数中不修改对象中的数据成员。
不要误认为常对象中的成员函数都是常成员函数。
常对象只保证所有数据成员的值不被修改。
如果在常对象中的成员函数未加const声明,编译系统把它作为非const成员函数处理。
还有一点要指出:
常成员函数不能调用另一个非const成员函数。
3、指向对象的常指针
定义:
将指向对象的指针变量声明为const型并将之初始化,这样指针值始终保持为其初不能改变,即其指向始终不变。
定义指向对象的常指针的一般形式为:
类名*const指针变量名=对象地址;
1、Timetl(10,12,15),t2;
//定义对象
Time*constptrl=t1
//const位置在指针变量名前面,规定ptrl的值是常值并指向对象t1,此后不能再改变指向;
ptrl=&
t2;
//错误,ptrl不能改变指向
2、也可以在定义指针变量时使之初始化。
3、指向对象的常指针变量的值不能改变,即始终指向同一个对象,但可以改变其所指向对象(如t1)中数据成员的值。
4、往往用常指针作为函数的形参,目的是不允许在函数执行过程中改变指针变量的值,使其始终指向原来的对象。
如果在函数执行过程中修改了该形参的值,编译系统就会发现错误,给出出错信息,这样比用人工来保证形参值不被修改更可靠。
4、指向常对象的指针变量
为了使读者更容易理解指向常对象的指针变量的概念和使用,首先了解指向常变量的指针变量,然后再进一步研究指向常对象的指针变量。
定义指向常变量的指针变量的一般形式为:
const类型名*指针变量名;
例:
下面定义了一个指向常变量的指针变量ptr:
constchar*ptr;
注意:
const的位置在最左侧,它与类型名char紧连,表示指针变量ptr指向的char变量是常变量,不能通过ptr来改变其值的。
(1)如果一个变量已被声明为常变量,只能用指向常变量的指针变量指向它,而不能用一般的(指向非const型变量的)指针变量去指向它。
(2)指向常变量的指针变量除了可以指向常变量外,还可以指向来被声明为const的变量。
此时不能通过此指针变量改变该变量的值。
(3)如果函数的形参是指向非const型变量的指针,实参只能用指向非const变量的指针,而不能用指向const变量的指针,这样,在执行函数的过程中可以改变形参指针变量所指向的变量(也就是实参指针所指向的变量)的值。
如果函数的形参是指向const型变量的指针,在执行函数过程中显然不能改变指针变量所指向的变量的值,因此允许实参是指向const变量的指针,或指向非const变量的指针。
使用形参和实参的对应关系见表2。
表2用指针变量作形参时形参和实参的对应关系
以上的对应关系与在
(2)中所介绍的指针变量和其所指向的变量的关系是一致的:
指向常变量的指针变量可以指向const和非const型的变量,而指向非const型变量的指针变量只能指向非const的变量。
以上介绍的是指向常变量的指针变量,指向常对象的指针变量的概念和使用是与此类似的,只要将“变量”换成“对象”即可。
(1)如果一个对象已被声明为常对象,只能用指向常对象的指针变量指向它,而不能用一般的(指向非const型对象的)指针变量去指向它。
(2)如果定义了一个指向常对象的指针变量,并使它指向一个非const的对象,则其指向的对象是不能通过指针来改变的。
(3)指向常对象的指针最常用于函数的形参,目的是在保护形参指针所指向的对象,使它在函数执行过程中不被修改。
(4)如果定义了一个指向常对象的指针变量,是不能通过它改变所指向的对象的值的,但是指针变量本身的值是可以改变的。
5、对象象的常引用
过去曾介绍:
一个变量的引用就是变量的别名。
实质上,变量名和引用名都指向同一段内存单元。
如果形参为变量的引用名,实参为变量名,则在调用函数进行虚实结合时,并不是为形参另外开辟一个存储空间(常称为建立实参的一个拷贝),而是把实参变量的地址传给形参(引用名),这样引用名也指向实参变量。
例8对象的常引用。
#include<
iostream>
usingnamespacestd;
classTime
{
public:
Time(int,int,int);
inthour;
intminute;
intsec;
};
Time(inth,intm,ints)//定义构造函数
{
hour=h;
minute=m;
sec=s;
}
voidfun(Time&
t)
//形参t是Time类对象的引用
t.hour=18;
}
intmain()
Timet1(10,13,56);
//tl是Time类对象
fun(t1);
//实参是Time类对象,可以通过引用来修改实参d的值
cout<
<
t1.hour<
endl;
//输出t1.hour的值为18
return0;
如果不希望在函数中修改实参t1的值,可以把引用t声明为const(常引用),函数原型为
voidfun(constTime&
t);
则在函数中不能改变t的值,也就是不能改变其对应的实参和t1的值。
在面向对象程序设计中,经常用常指针和常引用作函数参数。
这样既能保证数据安全,使数据不能被随意修改,在调用函数时又不必建立实参的拷贝。
在学习8节时会知道,每次调用函数建立实参的拷贝时,都要调用复制构造函数,要有时间开销。
用常指针和常引用作函数参数,可以提高程序运行效率。
6、const型数据的小结
本小节介绍const型数据和引用,利用它们可以对共用的数据进行保护。
由于与对象有关的const型数据种类较多,形式又有些相似,容易混淆,因此本节集中归纳一下。
为便于理解,以具体的形式表示,对象名设为Time。
通过表3可以对几种const型数据的用法和区别一目了然,需要时也便于查阅。
表中最后一行是对象的引用,不属于const型数据。
可能会觉得奉节介绍的内容虽不难理解,但难以记住,请不必着急,也不必硬记,在以后的实际应用中会逐步熟悉并熟练地使用它们。
二、对象的动态建立和释放
用前面介绍的方法定义的对象是静态的,在程序运行过程中,对象所占的空间是不能随时释放的。
例如在一个函数中定义了一个对象,只有在该函数结束时,该对象才释放。
但有时人们希望在需要用到对象时才建立对象,在不需要用该对象时就撤销它,释放它所占的内存空间以供别的数据使用。
这样可以提高内存空间的利用率。
C++语言用new运算符动态地分配内存,用delete运算符释放这些内存空间。
这也适用于对象,可以用new运算符动态建立对象,用delete运算符撤销对象。
new运算符动态地分配内存的一般格式:
对象的指针变量=new对象名
delete运算符释放这些内存空间的一般格式:
delete对象的指针变量
如果已经定义了一个Box类,可以用下面的方法动态地建立一个对象:
Box*pt
//定义一个指向Box类对象的指针变量p1
pt=newBox;
//在pt中存放了新建对象的起始地址
或:
newBox;
//对象既没有对象名,用户也不知道它的地址。
这种对象称为无名对象,它确实是存在的,但它没有名字。
1、编泽系统开辟了一段内存空间,并在此内存空间中存放一个Box类对象给pt指针。
在程序中就可以通过pt访问这个新建的对象。
如
pt->
height;
//输出该对象的height成
volume();
//调用该对象的volume函数,计算并输出体积
还允许在执行new时,对新建立的对象进行初始化。
如:
Box*pt=newBox(12,15,18);
2、调用对象既可以通过对象名,也可以通过指针。
用new建立的动态对象一般是不用对象名的,而是通过指针访问的,它主要应用于动态的数据结构,如链表。
访问链表中的结点,并不需要通过对象名,而是在上一个结点中存放下一个结点的地址,从而由上一个结点找到下一个结点,构成链接的关系。
3、在执行new运算时,如果内存量不足,无法开辟所需的内存空间,目前大多数编译系统都使new返回一个指针值。
只要检测返回值是否为0,就可判断分配内存是否成功。
AN61标准提出,在执行new出现故障时,就“抛出”一个“异常”,用户可根据异常进行有关处理(关于异常处理可参阅第8章)。
但c4-+标准仍然允许在出现new故障时返回。
指针值。
当前,不同的编译系统对new故障的处理方法是不同的。
4、在不再需要使用由new建立的对象时,可以用delete运算符予以释放。
deletePt;
//释放Pt指向的内存空间
这就撤销了pt指向的对象。
此后程序不能再使用该对象。
如果用一个指针变量pt先后指向不同的动态对象,应注意指针变量的当前指向,以免删错了对象。
5、在执行delete运算符时,在释放内存空间之前,自动调用析构函数,完成有关善后清理工作。
三、作业
1、P117:
7、8、9题。
2、实验:
四