第三章关于类和对象_第1页
第三章关于类和对象_第2页
第三章关于类和对象_第3页
第三章关于类和对象_第4页
第三章关于类和对象_第5页
已阅读5页,还剩140页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

第3章关于类和对象的进一步讨论

3.1构造函数

3.2析构函数

3.3调用构造函数和析构函数的顺序

3.4对象数组

3.5对象指针

3.6共用数据的保护

3.7对象的动态建立和释放

3.8对象的赋值和复制

3.9静态成员

3.10友元

3.H类模板

1/145

3.1构造函数(constructor:ctor)

如果定义一个变量,而程序未对其进行初始化的话,这个

变量的值是不确定的,因为c和C++不会自觉地去为它赋

值。与此相似,如果定义一个对象,而程序未对其数据成

员进行初始化的话,这个对象的值也是不确定的。

3.1.1对象的初始化

3.1.2构造函数的作用

3.1.3带参数的构造函数

3.1.4用参数初始化表初始化数据成员

3.1.5构造函数的重载

3.1.6使用默认参数的构造函数左0

#S

8Er小

2/145r„

:

311对象的初始化

•因为类是一种类型,是创建对象的蓝图,系统不会为它分

配内存空间。

•所以在定义类时,不能给数据成员赋初值,要在建立对象

时,给数据成员赋初值。否则对象的状态是不确定的。

如果类单输音缠成员是公有的,可以在定义对象时对

其数据嫡初始化。

例如:inthour;

intminute;在一个大括号内顺序列出各个公有

数据成员的值,在两个值之间用逗

intsec;号分隔。注意这只能用于数据成员

都是公有的情况。

);

Timetl{15,36,26);

3/145

z\在前面的例子里,是用成员函数对对象的数据成员赋初值,

V如果一个类定义了多个对象,对每个对象都要调用成员函

数对数据成员赋初值,那么程序就会变得烦琐,所以用成

员函数为数据成员赋初值绝不是一人好方法。按理说对象

的创建跟上帝造万物一样,万物诞生的时候都是有各自的

特征的。对象的初始属性应该是与生俱来的才是最自然的。

4/145

312构造函数的作用

C++提供了构造函数机制,用来为对象的数据成员进

行初始化。在前面一直未提及这个概念,其实如果你未

设计构造函数,系统在创建对象时,会自动提供一个默

认的构造函数,什么也不做。

构造函数用于为对象初始化,它属于某一个类,可以由系

统自动生成。也可以由程序员编写,程序员根据初始化的

要求设计构造函数及函数参数。

构造函数是一种特殊的成员函数,在程序中不需要写调用

语句,在系统建立对象时由系统自觉调用执行。

5/145

构造函数的特点:

(1)构造函数的名字与它的类名必须相同。

(2)它没有类型,也不返回值

(3)它可以带参数,也可以不带参数。

6/145

例3.1在例2.3的基础上定义构造成员函数

ttinclude<iostream>voidTime::set_time()

usingnamespacestd;

{cin>>hour;

classTime

cin>>minute;

(public:

Time()cin>>sec;

{hour=0;)

minute=0;

sec=0;

voidTime::show_time()

}

{

voidset_time();

voidshow_time();cout<<hour<<zz:〃:〃

private:<<sec<<endl;

inthour;

intminute;

intsec;

);

7/145

intmain()

在类Time中定义了构造函数

Time,它与所在的类同名。

Timetl;在建立对象时自动执行构造

tl.set_time();函数,该函数的作用是为对

象中的各个数据成员赋初值

tl.show_time();

0o注意只有执行构造函数

Timet2;时才为数据成员赋初值。

t2.show_time();

return0;

8/145

程序运行的情况为:

102554

10:25:54〃输出tl的值

0:0:0//输出t2的值

也可以在类内声明构造函数然后在类外定义构造函数。将

程序修改为:

Time();〃类内声明

然后在类外定义构造函数:

Time::Time()〃类外定义

{hour=0;

minute=0;

sec=0;

}9/145

1)关于构造函数的使用

7

(1)什么时候调用构造函数呢?当函数执行到对象401

句时建立对象,此时就要调用构造函数,对象就有了自己

的作用域,对象的生命周期开始了。

(2)构造函数没有返回值,因此不需要在定义中声明类型。

(3)构造函数不需要显式地调用,构造函数是在建立对象

日寸由系统自动执行的,且只执行一次。构造函数一般定义

为public。

(4)在构造函数中除了可以对数据成员赋初值,还可以使

用其他语句。

(5)如果用户没有定义构造函数,C++系统会自动生成一

个构造函数,而这个函数体是空的,不执行初始化操作。

10/145

313带形参数的构造函数

可以采用带参数的构造函数,使得对象取得初始确定值。

构造函数格式:

构造函数名(类型形参1,类型形参2,…)

Time(inth,intm,ints)

{hour=h;

minute二m;

sec二s;

}

在定义对象时指定实参,定义对象的格式为:

类名对象名(实参1,实参2,…);

Timetl(12,30,3禽45

例3.2有两个长方柱,其长、宽、高分别为:

(1)12z25z30

(2)15z30z21

编写程序,在类中用带参数的构造函数,计算它们的体积。

分析:可以在类中定义一个计算长方体体积的成员函数计

算对象的体积。

H

12/145

ttinclude<iostream>〃长方体构造函数

usingnamespacestd;Box::Box(inth,intw,intlen)

classBox{height=h;

{public:width=w;

Box(int,int,int);length=len;

intvolume();

//计算长方体的体积

private:

intBox::volume()

intheight;

intwidth;return(height^width*length);

intlength;

);

13/145

intmain()

Boxboxl(12,25,30);//定义对象boxl

cout〈〈〃boxl体积=〃〈〈boxl.volume()<<endl;

Boxbox2(15,30,21);//定义对象box2

cout<<〃box2体积=〃〈〈box2.volume()<<endl;

return0;

}____________________________________________

O提醒:

(1)带形参的构造函数在定义对象时必须指定实参

(2)用这种方法可以实现不同对象的不同的初始化。

14/145

Note

314用参数初始化表对数据成员初始化

C++提供了参数初始化表的方法对数据成员初始化。这种方法不必在构

造函数内对数据成员初始化,在函数的首部就能实现数据成员初始化。

函数名(类型1形参1,类型2形参2):成员名1(形参1),成员名2(形参2)

{}〃函数体可以为空。

Time(inth,intm,ints):hour(h),minute(m),sec(s)

功能:执行构造函数时,将形参1的值赋予成员1,将形参2的值赋予成

员2,形参的值由定义对象时的提供的实参值决定。

此时定义对象的格式依然是带实参的形式:

类名对象名(实参1,实参2);

Timetl(8,7,6);

15/145

:

例:定义带形参初始化表的构造函数

Box::Box(inth,intw,intlen):height(h),

width(w),length(len){}||

冒号作为分隔符

定义对象:

Boxboxl(12,25,30);|

...逗号分隔的初始化列表

Boxbox2(15,30,21);

16/145

315构造函数的重载

构造函数也可以重载。一个类可以有多个同名构造函数,

函数参数的个数、参数的类型各不相同。

例3.3:在例3.2的基础上定义两个构造函数其中一个无参

数,另一个有参数。

17/145

ttinclude<iostream>

usingnamespacestd;

classBox

{public:

Box();x.

Box(inth,,intlen)height(h),

width(w),length(leSl{}

intvolume();

private:\

intheight;\Box::Box()

intwidth;\{height=10;

}intBox::volume()

intlength;\wliedntght=h1=01;0;

{return(height*width*length);

);

I18)145

intmain()

Boxboxl;

cout<<z/boxl体积二〃<<boxl.volume()<<endl;

Boxbox2(15,30,25);

cout<<zzbox2体积二〃<<box2.volume()<<endl;

return0;

例子中定义了两个构造函数,一个无参数另一个带三个参

数。系统根据对象定义的形式决定调用哪个构造函数。

对象boxl没有实参,系统调用无参数的构造函数;对象box2

带三个实参,系统调用带形参的构造函数。

说明:

_(1)不带形参的构造函数为默认构造函数,每个类只有一

一个默认构造函数,如果只定义无参对象,则系统自动提供

空白构造函数。如果既有无参对象也有带参对象,必须人

为定义一个无参构造函数。

(2)虽然每个类可以包含多个构造函数,但是创建对象时,

系统仅执行其中一个。

20/145

316使用默认参数值的构造函数

•C++允许在构造函数里为形参指定默认值,如果创建对象时,

未给出相应的实参时,系统将用形参的默认值为形参赋值。

格式:

函数名(类型形参上常数,类型形参2二常数,…);

例3.4:将例3.3中的构造函数改用带默认值的参数,长、宽、

高的默认值都是10。

21/145

ttinclude<iostream>

usingnamespacestd;

classBox

(public:

Box(intw=10,inth=10,intlen=10);

intvolume();

private:

intheight;

intwidth;

intlength;

);

22/145

Box::Box(intw,inth,intlen)

{height=h;

width=w;

length=len;

}

intBox::volume()

{return(height*width*length)

23/145

intmain()

(

Boxboxl;

cout<<z/boxl体积=z,<<boxl.volume()<<endl;

Boxbox2(15);

cout<<z/box2体积zz<<box2.volume()<<endl;

Boxbox3(15,30);

cout<<z/box3体积zz<<box3.volume()<<endl;

Boxbox4(15,30,20);

cout<<z/box4体积〃〈〈box4.volume()<<endl;

return0;

24/145

程序运行结果为:

boxl体积=1000

box2体积=1500

box3体积=4500

box4体积二9000

构造函数也可以改写成带参数初始化表的形式:

Box::Box(inth,intw,intlen):height(h),

width(w),length(len){}

整个函数只需一行,简单方便。

25/145

在构造函数中使用默认参数提供了建立对象的多种选择,

它的作用相当于多个重载构造函数。

说明:

(1)如果在类外定义构造函数,应该在声明构造函数时指

足默认参数值,在定义函数时可以不再指定默认参数值。

在相反情况下,如果定义了无参对象,则编译器报错:没

有合适的构造函数。

(2)在声明构造函数时,形参名可以省略例如:

Box(int=10,int=10,int=10);

等号不可省略。、

26/145

(3)如果构造函数的所有形参都指定了默认值,在定义对

象时,可以指定实参也可不指定实参。由于不指定实参也

可以调用构造函数,因此全部形参都指定了默认值的构造

函数也属于默认构造函数。为了避免歧义,不允许同时定

义不带形参的构造函数和全部形参都指定默认值的构造函

数。

(4)同样为了避免歧义性,如定义了全部形参带默认值的

构造函数后,不能再定义重载构造函数。反之亦然。

27/145

Box(int=10,int=10,int=10);

Box();

Box(int,int);

若定义对象:

Boxboxl;

Boxbox2(15,30);

这时应该调用哪个构造函数呢?J

如果构造函数中参数并非都带默认值时,要具体分析情况。

如有以下三个原型声明:

28/145

Box();

Box(int,int=10,int=10);

Box(int,int);

若定义对象:

Boxboxl;〃正确,调用第一个

Boxbox2(15);//调用第二个

Boxbox3(15,30);//不知调用哪一个

因此不要同时使用重载构造函数和带默认值的构造函数。

n

29/145W

3.1构造函数

3.2析构函数

3.3调用构造函数和析构函数的顺序

3.4对象数组

3.5对象指针

3.6共用数据的保护

3.7对象的动态建立和释放

3.8对象的赋值和复制

3.9静态成员

3.10友元U

3.11类模板.

30/145S

3.2析构函数(destructor:dtor)

析构函数也是个特殊的成员函数,它的作用与构造函数相

反,当对象的生命周期结束时,系统自动调用析构函数,

收回对象占用的内存空间。

执行析构函数的时机:

①在一个函数内定义的对象,当这个函数结束时,自动执行

析构函数释放对象。

②static局部对象要到main函数结束或执行exit命令时才

自动执行析构函数释放对象。

③全局对象(在函数外定义的对象)当main函数结束或执

行exit命令时自动执行析构函数释放对象。

④如果用new建立动态对象,用delete时自动执行析构函数

释放对象。

31/145

析构函数的特征

①析构函数名以符号开始后跟类名

②析构函数没有数据类型、返回值、形参。由于没有形参

所以析构函数不能重载。一个类只有一个析构函数。

③如果程序员没有定义析构函数,C++编译系统会自动生成

一个析构函数。

析构函数除了释放对象(资源)外,还可以执行程序员在

最后一次使用对象后希望执行的任何操作。例如输出有关

的信息。

32/145

例3.5包含构造函数和析构函数的C++程序

ttinclude<iostream>

ttinclude<string>

usingnamespacestd;

classStudent

{public:

Student(intn,stringnam,chars)

{num=n;

name=nam;

sex二s;

cout<<z/Construetorcalled.z/<<endl;

33/145

Student()

{cout<<z,Destruetorcalled.z/<<endl;}

voiddisplay()

{cout<<z/num:z/<<num<<endl;

cout<<z/name:zz<<name<<endl;

cout<<z/sex:z/<<sex<<endl<<endl;

}

private:

intnum;

stringname;

charsex;

);

34/145

intmain()

{Studentstudl(10010,z,Wang_liz,,,f?);

studl.display();

Studentstud2(10011,〃Zhang_fun〃,';

stud2.display();

return0;

n

35/145

main函数前声明的类其作用域是全局的。

程序运行结果如下:

Constructorcalled.

num:10010

name:Wang_li

sex:f

Constructorcalled.

num:10011

name:Zhangfun

sex:m

Destructorcalled.

Destructorcalled.

36/145

3.1构造函数

3.2析构函数

3.3调用构造函数和析构函数的顺序

3.4对象数组

3.5对象指针

3.6共用数据的保护

3.7对象的动态建立和释放

3.8对象的赋值和复制

3.9静态成员

3.10友元■

类模板Cm

3.HC

Mi

37/145.蠢

3.3调用构造函数和析构函数的顺序

•在使用构造函数和析构函数时需要特别注意对它们的调用

时间和调用顺序。

•在一般情况下,调用析构函数的次序与调用构造函数的次

序恰好相反:最先调用构造函数的对象,最后调用析构函

数。而最后调用构造函数的对象,最先调用析构函数。可

简记为:先构造的后析构,后构造的先析构,它相当于一

个栈,后进先出。

38/145

对象1的对象1的

构造函数析构函数

对象2的对象2的

构造函数析构函数

对象3的[对象3的

构造函数"I析构函数

39/145

构造析构顺序总结:

在一般情况下,调用析构函数的次序与调用构造函数

的次序相反,这是对同一类存储类别的对象而言的。

⑴在全局范围中定义的对象(在所有函数之外定义的对

象),在主函数执行前调用构造函数。当主函数结束时,

调用析构函数。

⑵在函数中定义静态局部对象,则在第一次调用该函

数建立对象时调用构造函数,在主函数结束或调用

exit函数时才调用析构函数。

⑶局部自动对象,在创建对象时调用构造函数。如多次调

用对象所在的函数,则每次创建对象时都调用构造函数。

在函数调用结束时调用析构函数。

40/145

例如:

Voidfn()

{studentstl;〃定义局部自动对象

staticstudentst2;〃定义静态局部对象

•••

)

对象stl是每次调用函数fn时调用构造函数,在函数fn结

束时调用析构函数。

对象st2是第一次调用函数fn时调用构造函数,在函数fn

结束时并不调用析构函数,在到主函数结束时才调用析构

函数。

41/145

3.1构造函数

3.2析构函数

3.3调用构造函数和析构函数的顺序

3.4对象数组

3.5对象指针

3.6共用数据的保护

3.7对象的动态建立和释放

3.8对象的赋值和复制

3.9静态成员

3.10友元

3.H类模板

42/145

3.4对象数组(较少使用)

类是一种特殊的数据类型,它当然是C++的合法类型,

自然就可以定义对象数组。在一个对象数组中各个元素

都是同类对象例如一个班级有50个学生,每个学生具有

学号、年龄、成绩等属性,可以为这个班级建立一个对

象数组,数组包括了50个元素:

studentstd[50];

给数组初始化时,不可以用如下形式:

Studentstu[3]={1,7,6};

43/145

错误提示:分别在DevC++和VC++6.0下

23};

24intmain()

{Studentstu[3]={l,7,6};

5.5八..,犍QfnrnldiQnln\fCy•

intmain()

〈Studentstu[3]=<1,7,6};

stu[0].displayO;

stu[1].displayO;

stu[2].displayO;

return0;

}

I

■ration:defaultparal-Win32Debug

|MyProjects\defaultpara1\dest・cpp(26)::errorC244G:'initializing':cannotconuertfrom'constint'to'classStudent

tudioVMyProiecl.conversionfrom'inftonon-scalartype'Student1requested

tudio\MyProject..conversionfrom'inftonon-scalartype'Student1requested

44/145

数组的正确初始化

如果构造函数有多个参数,在花括号中为每个对象分别写

上构造函数并指定实参。格式为:

studentst[n]={student(实参1,实参2,实参3);

••••••

student(实参1',实参2',实参3');

);

45/145

初始化数组样例

假定对象有三个数据成员:学号、年龄、成绩。下面定义

有三个学生的对象数组:

studentstd[3]={student(1001,18,87),

student(1002,19,76),

student(1003,18,80)

);〃构造函数带实参

在建立对象数组时,分别调用构造函数,对每个对象初始

化。每个元素的实参用括号括起来,实参的位置与构造函

数形参的位置一一对应。

46/145

例3.6对象数组的使用方法。(长方体数组)

#include<iostream>

usingnamespacestd;

classBox

{public:

Box(inth=10zintw=12,intlen=15):height(h)/width(w)/length(len)

{}〃带默认参数值和参数表

intvolume();

private:

intheight;

intwidth;

intlength;

);

47/145

intBox::volume()

{return(height*width*length);}

intmain()

Boxa[3]={Box(10,12,15),〃每个数组元素是一个对象

Box(15,18,20),

Box(16,20,26)};

cout<<”a[0]的体积是"<<a[0].volume()<<endl;

cout<<”a[l]的体积是"<<a[l].volume()<<endl;

cout<<”a[2]的体积是"<<a[2].volume()<<endl;

return0;

)

48/145

3.1构造函数

3.2析构函数

3.3调用构造函数和析构函数的顺序

3.4对象数组

3.5对象指针

3.6共用数据的保护

3.7对象的动态建立和释放

3.8对象的赋值和复制

3.9静态成员

3.10友元

3.H类模板

49/145

3.5对象指针

指针的含义是内存单元的地址,可以指向一般的变量,也

可以指向对象。

50/145

3.5.1指向对象的指针

3.5.2指向对象成员的指针

3.5.3this指针

51/145

351指向对象的指针

■对象在内存占据一片连续的内存空间,CPU都按地址访问内

存,对象在内存的起始地址称为对象指针。

定义对象的指针变量以保存对象的地址,方便引用对象成员,

格式如下:

类名*变量名;

Student*pstu;

定义好指针变量后,必须先给赋予合法的地址后才能使用。

一般是通过取地址运算取得另外一对象的地址,然后赋给

指针变量。

八Studentstul;

'•\pstu=&stul;

要用与对象类型相同的指针变量保存运算的结果。

rfB㈤F

52/145

classTimevoidTime::set_time()

(public:{cin>>hour;

Time()cin>>minute;

{hour=0;cin>>sec;

minute=0;}

sec=0;voidTime::show_time()

){cout<<hour<</z:〃:〃〈〈sec〈

voidset_time();<endl;

voidshow_time();

private:

inthour;

intminute;

intsec;

);

53/145

在此基础上,有以下语句:

intmain()

(Time*pt;//定义pt是指向Time类对象的指针

Timetl;//定义Time类对象tl

pt=&tl;//将对象tl的地址赋予pt

(*pt).show_time()〃用指针变量访问对象的成员。

pt->show_time()

54/145

3.5.2指向对象成员的指针

对象由成员组成。对象占据的内存区是各个数据成员占据

的内存区的总和。对象成员也有地址,即指针。

「指向数据成员的指针

指针

I指向成员函数的指针。

H

55/145

1.指向对象公有数据成员的指针

①定义数据成员的指针变量

数据类型*指针变量名

②取得公有数据成员的地址

&对象名.成员名

这里的数据类型是数据成员的数据类型。

例:

Timetl;

int*pl;//定义一个指向整型数据的指针变量

pl=&tl.hour;〃假定hour是公有成员

cout<<*pl<<endl;

,二

❶实际上类的数据成员是不公开的。所以这个方法不常用。

56/145

2.指向对象成员函数的指针

①定义指向成员函数的指针变量

数据类型(类名::*变量名)(形参表);

void(Student::*pf)();

数据类型:成员函数的类型。

类名:对象所属的类

变量名:按标识符规则取名

形参表:指定成员函数的形参表(形参个数、类型)

②取成员函数的地址

&类名::成员函数名

&Student::display

在VC++系统中,也可以不写&。为了与C语言一致,建议不要省略

57/145

③给指针变量赋初值

指针变量名二&类名::成员函数名;

④用指针变量调用成员函数

(对象名.*指针变量名)([实参表]);

对象名:是指定调用成员函数的对象。

*:明确其后的是一个指针变量。

实参表:与成员函数的形参表对应,如无形参,可以省略

实参表。

3

58/145

例3.7有关对象指针的使用方法

ttinclude<iostream>Time::Time(inth,intm,ints)

usingnamespacestd;{hour=h;

classTimeminute=m;

{public:sec=s;

Time(int,int,int)

voidTime::get_time()

inthour;

intminute;{cout<<hour<<z/://<<minute<</,:/z<<sec<

intsec;<endl;}

voidgettime();

为了比较指向数据成员和函数成员的指针。

把数据成员也声明为公有的。一般不要这

么做。

59/145

intmain()

{Timetl(10,13,56);

int*pl=&tl.hour;//定义指向成员的指针pl

cout<<*pl<<endl;

tl.get_time();//调用成员函数

Time*p2=&tl;//定义指向对象tl的指针p2

p2->get_time();〃用对象指针调用成员函数

void(Time::*p3)();71定义指向成员函数的指针

p3=&Time::gettime;/给成员函数的指针赋值

(tl.*p3)();//用指向成员函数的指针调用成员函数

retur

,/;1-V,

函数成员指针的声明赋值和引用。

60/145

程序运行结果为:

10〃*pl的值

10:13:56//tl.get_time();的执行结果

10:13:56//p2->get_time();的执行结果

10:13:56//(tl.*p3)();的执行结果

程序采用了三种方法输出tl的hour,minute,sec的值

说明:

(1)成员函数的起始地址的正确表示是:

&类名::成员函数名。

不要写成:p3=&tl.get_time;

但是写成:P3=tl.get_time;倒是可以,请思考为什么?

(2)主函数的第8和9行可以合并写成:

void(Time::*p3)()=&Time::gettime;

61/145

3.5.3this指针

一个类的成员函数只有一个内存拷贝。类中不论哪个对象调用

某个成员函数,调用的都是内存中同一个成员函数代码。例如

Time类一个成员函数:

voidTime::get_time()

{cout<<hour<<z/:〃:'z<<sec<<endl;}

tl.get_time();

t2.get_time();

当不同对象的成员函数访问数据成员时,怎么保证访问的就是

指定对象的数据成员?

S其实每个成员函数中都包含一个特殊的指针,它的名字

是thiso它是指向本类对象的指针,

62/145

当对象调用成员函数时,它的值就是该对象的起始地址。

所以为了区分不同对象访问成员函数,语法要求调用成员

函数格式是:

intB熨镰・鹰函数名(实参表)

从语法搬鲍浦搞锦标豳廨用成员函数。

程悌霆甄印编译器把对象的地址即this指针放在ECX寄

赣霰麻瞑懿硼口成员函数定义如下:

{return(this->height*this->width*this->length);}

63/145

对于计算长方体体积的成员函数volume,当对象a调用它时,

就把对象a地址给this指针,编译程序将a的地址作为实参传

给成员函数:

a.volume(&a);

函数体变成:

(this->height)*(this->width)*(this->length)

等价于:(a.height)*(a.width)*(a.length)

C++通过编译程序,在对象调用成员函数时,把对象的地

址赋予this指针,用this指针指向对象,实现了用同一

个成员函数访问不同对象的数据成员。

64/145

可以用(*this)表示调用成员函数的对象。

(*this)就是this所指的对象。如前面的计算长方体体积

的函数中return语句可以写成:

return((*this).height*(*this).width*(*this).

length);

注意this两侧的括号不能省略,不能写成:

return(*this.height**this.width**this.length);

根据运算符优先级语句被翻译成:*(this,height),而

this.height是非法的,编译将报错。

65/145

:

3.1构造函数

3.2析构函数

3.3调用构造函数和析构函数的顺序

3.4对象数组

3.5对象指针

3.6共用数据的保护

3.7对象的动态建立和释放

3.8对象的赋值和复制

3.9静态成员

3.10友元

3.H类模板

66/145

3.6共用数据的保护

如果既希望数据在一定范围内共享,又不愿它被随意修改,

从技术上可以把数据指定为只读型的。C++提供const手段,

将数据、对象、成员函数指定为常量,从而实现了只读要

求,达到保护数据的目的。

67/145

3.6.1常对象

3.6.2常对'象成员

3.6.3指向对象的常指针

*3.6.4指向常对象的指针变量

3.6.5对象的常引用

3.6.6const型数据小结

68/145

3.6.1常对象

菽:const类名对象名(实参表);

或:类名const对象名(实参表);

把对象定义为常对象,对象中的数据成员就是常变量,在

定义时必须给数据成员的初值,因为在程序中不允许修改

常对象的数据成员值。

如果常对象的成员函数不是常成员函数(除构造函数和析

构函数外),则外界不能调用此对象的成员函数。

如:constTime11(10,15,36);

tl.get_time();//错误,不能调用

为了访问常对象中的数据成员,要定义常成员函数:1

voidget_time()const-

即使加上了const,该函触不能修改对象数据成员。

如果在常对象中要修改某个数据成员,C++提供了指定可变

的数据成员方法。

格式:mutable类型数据成员;

在定义数据成员时加mutable后,将数据成员声明为可变的

数据成员,就可以用声明为cons珀勺成员函数修改它的值。

H

70/145

3.6.2常对象成员

可以在声明普通对象时将数据成员或成员函数声明为常数

据成员或常成员函数。

71/145

1.常数据成员

格式:const类型数据成员名

将类中的数据成员定义为具有只读的性质。

注意:只能通过带参数初始表的构造函数对常数据成员进

行初始化。

例:constinthour;

Time::Time(inth)

{hour=h;…}〃错误

应该写成:

Time::Time(inth):hour

72/145

在类中声明了某个常数据成员后,该类中每个对象的这个

数据成员的值都是只读的,而每个对象的这个数据成员的

值可以不同,由定义对象时给出。

73/145

2.常成员函数

定义格式:

类型函数名(形参表)const;

const是函数类型的一部分,在声明函数原型和定义函数

时都要用const关键字。

74/145

使用常成员函数需注意:

“const是函数类型的一个组成部分,因此在函数的实现部分

也要使用关键字ccmsto

0常成员函数不能修改对象的数据成员,也不能调用该类中

没有由关键字const修饰的成员函数,从而保证了在常成员

函数中不会修改数据成员的值。

0如果一个对象被说明为常对象,则只能调用它的常成员函

数。

般成员函数可以访问或修改本类中的非const数据成员。而常成员函

数只能读本类中的数据成员,而不能写它们。

75/145

成员函数与成员数据的关系

HIXJ吊数据成员.二常函数成员

:、、/

、、XZ

、t常对象数据成员

---------A读操作实线表示可操作

V--------写操作虚线表7F不用操作

76/145

常成员函数小结:

⑴如果类中有部分数据成员的值要求为只读,可以将它们

声明为const,这样成员函数只能读这些数据成员的值,但

不能修改它们的值。

⑵如果所有数据成员的值为只读,可将对象声明为const,

在类中必须声明const成员函数,常对象只能通过常成员

函数读数据成员。

⑶常对象不能调用非const成员函数。

提醒:如果常对象的成员函数未加const,编译系统将其当

作非const成员函数;常成员函数不能调用非const成员函

3

77/145

3.6.3指向对象的常指针

如果在定义指向对象的指针时,使用了关键字const,匕

就是一个常指针,必须在定义时对其初始化。并且在程序

运行中不能再修改指针的值。

类名*const指针变量名二对象地址

例:

Timetl(10,12,15)zt2;

Time*constpl=&tl;

在此后,程序中不能修改Pl。

例:P>&t2;//错误语句;

0L

#3

8

78/145一

O指向对象的常指针,在程序运行中始终指的是同一个对象。

即指针变量的值始终不变,但它所指对象的数据成员值可

以修改。当需要将一个指针变量固定地与一个对象相联系

时,就可将指针变量指定为const。往往用常指针作为函数

的形参,目的是不允许在函数中修改指针变量的值,让它

始终指向原来的对象。

79/145

364指向常对象的指针变量

格式:

const类型名*指针变量名;

例:constStudent*p;〃指向const对象。

指向对象

const非const指针变量

的指针变量

对象

mEconst_^6句指向&可修改

因为const指针变量约束不可指向

比较严格,所以可以指

向任意类型对象。80/145—»可指向&不可修改所

指对象成员

ftinclude<iostream>

ttinclude<string>

usingnamespacestd;

classStudent

{public:

Student(intn,stringnam,chars)

{num=n;

name=nam;

sex-s;

cout<<^Constructorcalled.z/<<num<<endl;}

〜Student()

{cout<<z/Destruetorcalled./z<<num<<endl;}

voidsetdata()

{cin>>num;}

voiddisplay()const;

private:

intnum;

voidStudent::display()const

stringname;{cout<<,,num:,,<<num<<endl;

charsex;cout<<,,name:z,<<name<<endl;

);cout〈〈〃sex:,/<<sex<<endl<<endl;

intmain()

{constStudenttl(1,〃yang〃,'f');

Studentt2(2,〃chao〃,'f');

constStudent*p;

Student*q;

p二&tl;〃指向const对象tl的指针变量。

p->display();

p=&t2;〃指向const对象的指针变量可以指向非const对象t2。

p->display();

//q=&tl;〃出错,指向普通对象的指针变量不可以指向const对象。

//q->display();

q=&t2;〃指向普通对象的指针变量只可以指向普通对象。

q->display();

//p->setdata();通过指向const型的指针变量调用对象成员函数,无法修

改成员。

//t2.setdata();〃但是通过对象调用成员函数,则可以修改成员变量。

q->setdata();〃这个也是同样道理。因为q不是指向常量对象的指针,

所以,可以调用能够修改成员变量的函数。

return0;

82/145

3.6.5对象的常引用

前面学过引用是传递参数的有效办法。用引用形参时,形

参变量与实参变量是同一个变量,在函数内修改引用形参

也就是修改实参变量。

如果用引用形参又不想让函数修改实参,可以使用常引用

机制。

格式:const类名&形参对象名

如:constStudent&stu

83/145

例3.8对象的引用

ttinclude<iostream>voidTime::gettime()

usingnamespacestd;(

classTimecout<<hour;

{public:cout<<minute;

Time(int,int,int);cout<<sec;

voidsettime();

voidgettime();

voidTime::settime()

private:(

inthour;cin>>hour;

intminute;cin>>minute;

intsec;cin>>sec;

);

Time::Time(inth,intm,ints)

{hour=h;

minute=m;

SeC=S;}84/145

voidfun(Time&t)//引用型形参,如果参数前面加

const,则不能修改所引用对象的成员数据。

{t.settime();

)

intmain()

{Timetl(10,13,56);

tl.gettime();

fun(tl);〃传递对象的地址

tl.gettime();

return0;

85/145

3.6.6const型数据的小结企

表3.3

形式含义

Timeconsttl;

tl是常对象,其值在任何情况下都不能改变

或constTimetl;

voidTime::fun()constfun是Time类中的常成员函数,可以引用,但不能修改本类中的数据成员

Time*constp;P是指向Time对象的常指针,p的值(即p的指向)不能改变

constTime*p;P是指向Time类常对象的指针,其指向的类对象的值不能通过指针来改变

Time&tl=t;tl是Time类对象t的引用,t和tl指向同一段内存空间

诀窍:从变量开始往左推导,即可得知一个变量是什么类型的。星号左边是

要指向的东西,右边都是修饰指针变量的。如:

Time*constp;p左边是const的,表示p不能变,再往左是星号,说明p

是一个指针,再往左是类名,表示指向那样一个类的对象,成员可以修改。

Timeconst*p;p左边是星号,表示p是一个指针,指向的东西是const的,

不能变的,再往左是类名,表示指向那样一个类的对象,其成员不能修改。

O0/T40

3.1构造函数

3.2析构函数

3.3调用构造函数和析构函数的顺序

3.4对象数组

3.5对象指针

3.6共用数据的保护

3.7对象的动态建立和释放

3.8对象的赋值和复制

3.9静态成员

3.10友元

2

3.n类模板/8•

£

8

f艮

87/145-一

3.7对象的动态建立和释放

0C++提供了new和delete运算符,实现动态分配、回收内存。它们也可以

用来动态建立对象和释放对象。

1.动态建立对象格式:new类名;

功能:在堆里分配内存,建立指定类的一个无名对象。

如果分配成功,将返回动态对象的起始地址(指针);

如不成功,返回0。为了保存这个指针,必须事先定义

类类型的指针变量。

格式:类名*指针变量名;

例:Box*pt;

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论