第17章C++类:第2部分_第1页
第17章C++类:第2部分_第2页
第17章C++类:第2部分_第3页
第17章C++类:第2部分_第4页
第17章C++类:第2部分_第5页
已阅读5页,还剩186页未读 继续免费阅读

下载本文档

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

文档简介

第17章C++类:第二部分

目标

•可以动态创建和破坏对象。

,可以指定const(常量)对象和const成员函数。

•理解friend函数和友类的目的。

•理解如何使用static数据成员和成员函数。

•理解容器类的概念。

•理解可以遍历容器类的元素的迭代器类的概念。

•理解this指针的使用。

17.1简介

在本章中,我们继续研究类和数据抽象。我们在第18章中将讨论更多的高级内容,并建

立类和运算符重载讨论的基础。第16章~第18章的讨论鼓励程序员使用对象,我们称之为基

于对象的编程方法(OBP,object-basedprogramming)0然后,第19章和第20章介绍了继承

和多态性,这是真正面向对象的编程技术(OOP,object-orientedprogramming)..在本章和后

面的章节中,我们使用在第8章中介绍的C样式的字符串,这将帮助您掌握C指针的复杂内

容,准备进入专业世界,在这里,您将看见过去20年中所遗留的大量C代码。

17.2const(常量)对象和const成员函数

我们已经强调了,最低特权原则是好的软件工程所用的最基本原则之一。下面研究这个原

则如何应用于对象。

一些对象需要修改,而另外一些不需要。程序员可以使用关键字const来指定对象不能修

改,而尝试修改该对象将导致语法错误。例如:

constTimenoon(12,0,0);

声明了类Time的const对象noon,并将其初始化为12。

软件工程经验17.1__________________________________________________________

将对象声明为const有助于实施最低特权原则。这样,在编译时可以捕获修改对

象的尝试,而不是导致执行期间错误。

软件工程经验17.2__________________________________________________________

使用const对于正确的类设计、程序设计和编码是非常重要的。

性能提示17.1______________________________________________________________

声明变量和对象是const并不仅仅是有效的软件工程方法,因为现在的高级优化

编译程序可以对常量执行某些优化,而不能对变量进行优化,因此它也可以改善

性能。

C++编译程序不允许任何成员函数调用const对象,除非成员函数本身也声明为const。这

甚至对于不修改对象的get成员函数也是一样的。声明为const的成员函数不会修改对象,编

译程序不允许这样。

在函数原型中,通过在函数的参数列表后面插入关键字const,或者在函数定义中,在函

数体开始的左大括号之前加入关键字const,就可以在原型和定义中指定函数是const。例如,

下面的类A的成员函数:

intA::getValue()const{returnprivateDataMember;}

仅仅返回对象的一个数据成员的值,而且正确声明为const。

常见的编程错误17.1________________________________________________________

将修改对象数据成员的成员函数定义为const将导致语法错误。

常见的编程错误17.2________________________________________________________

在类的相同实例上,如果将调用类的非const成员函数的成员函数定义为const,

将导致语法错误。

常见的编程错误17.3________________________________________________________

在const对象上调用非const成员函数将导致语法错误。

软件工程经验17.3__________________________________________________________

const成员函数可以用非const版本来重载。选择使用哪一个重载成员函数是由编

译程序根据对象是否是const来决定的。

这里提出了有关构造函数和析构函数的有趣问题,它们每一个都需要经常修改对象。并且

不允许对const对象的构造函数和析构函数使用const声明。必须允许构造函数修改对象,这

样可以正确地初始化对象。析构函数必须可以在破坏对象之前终止清理杂务。

常见的编程错误17.4________________________________________________________

产尝试声明构造函数或者析构函数为const将导致语法错误。

图17.1实例化了两个Time对象:一个是非const对象,另一个是const对象。该程序尝

试用非const成员函数setHour(第102行)和printstandard(在第108行)来修改const对象

noon。程序也说明了在对象上调用成员函数的3个其他组合:在非const对象上调用非const

成员函数(第100行),在非const对象上调用const成员函数(第104行),以及在const对象

上调用const成员函数(第106行和107行)。两个流行的编译程序为非const成员函数在const

544第2部分C++编程

对象上调用而产生的消息显示在输出窗口中。

1//Fig.17.1:time5.h

2//DeclarationoftheclassTime.

3//Memberfunctionsdefinedintime5.cpp

4#ifndefTIME5_H

5#defineTIME5_H

6

7classTime{

8public:

9Time(int=0,int=0,int=0);//defaultconstructor

10

11//setfunctions

12voidsetTime(int,int,int);//settime

13voidsetHour(int);//sethour

14voidsetMinute(int);//setminute

15voidsetSecond(int);//setsecond

16

17//getfunctions(normallydeclaredconst)

18intgetHour()const;//returnhour

19intgetMinute()const;//returnminute

20intgetSecond()const;//returnsecond

21

22//printfunctions(normallydeclaredconst)

23voidprintMilitary()const;//printmilitarytime

24voidprintstandard();//printstandardtime

25private:

26inthour;//0-23

27intminute;//0-59

28intsecond;//0-59

29);

30

31#endif

图17.1通过const对象和const成员函数来使用Time类:time5.h

32//Fig.17.1:time5.cpp

33//MemberfunctiondefinitionsforTimeclass.

34#include<iostream>

35

36usingstd::cout;

37

38#include"time5.hn

39

40//Constructorfunctiontoinitializeprivatedata.

41//Defaultvaluesare0(seeclassdefinition).

42Time::Time(inthr,intmin,intsec)

43{setTime(hr.min.sec);)

44

45//Setthevaluesofhour,minute,andsecond.

46voidTime::setTime(inth,intm,ints)

47(

48setHour(h);

49setMinute(m);

50setSecond(s);

51}

52

53//Setthehourvalue

54voidTime::setHour(inth)

55{hour=(h>=0&&h<24)?h:0;}

56

57//Settheminutevalue

58voidTime::setMinute(intm)

59{minute=(m>=0&&m<60)?m:0;}

60

61//Setthesecondvalue

62voidTime::setSecond(ints)

63{second=(s>=0&&s<60)?s:0;}

64

65//Getthehourvalue

66intTime::getHour()const{returnhour;}

67

68//Gettheminutevalue

69intTime::getMinute()const{returnminute;}

70

71//Getthesecondvalue

72intTime::getSecond()const{returnsecond;}

73

74//Displaymilitaryformattime:HH:MM

75voidTime::printMilitary()const

76(

77cout<<(hour<10?*'0'*:,***)<<hour<<

78<<(minute<10?*'0'*:"")<<minute;

79)

80

81//Displaystandardformattime:HH:MM:SSAM(orPM)

82voidTime::printstandard()//shouldbeconst

83

84cout<<((hour==12)?12:hour%12)<<n:n

85<<(minute<10?"O',nn)<<minute<<“:"

86<<(second<10?n0n““)<<second

87<<(hour<12?nAM"HPM”)

88

图17.1通过const对象和const成员函数来使用Time类:lime5.cpp

89//Fig.17.1:figl7_01.cpp

546第2部分C++编程

90//Attemptingtoaccessaconstobjectwith

91//non-constmemberfunctions.

92#include"time5.hn

93

94intmain()

95(

96TimewakeUp(6,45,0);//non-constantobject

97constTimenoon(12,0,0);//constantobject

98

99//MEMBERFUNCTIONOBJECT

100wakeUp.setHour(18);//non-constnon-const

101

102noon.setHour(12);//non-constconst

103

104wakeUp.getHour();//constnon-const

105

106noon.getMinute();//constconst

107noon.printMilitary();//constconst

108noon.printstandard();//non-constconst

109return0;

110}

BorlandC++命令行编译程序警告消息

Figl7_01.cpp:

WarningW8037Figl7_01.cpp14:Non-constfunctionTime::setHour(int)

calledforconstobjectinfunctionmain()

WarningW8037Figl7_01.cpp20:Non-constfunctionTime::printStandard()

calledforconstobjectinfunctionmain()

TurboIncrementalLink5.00Copyright(c)1997,2000Borland

MicrosoftVisualC++编译程序错误消息

Compiling...

Figl7_01.cpp:

d:figl7_01.cpp(14):errorC2662:*setHour*:cannotconvert*this*

pointerfrom*constclassTime*to'classTime&*

Conversionlosesqualifiers

d:\figl7_01.cpp(20):errorC2662:1printStandard*:cannotconvert

fthis*pointerfrom1constclassTime1to*classTime&'

Conversionlosesqualifiers

Time5.cpp

Errorexecutingcl.exe

Test.exe-2error(s),0warning(s)

图17.1通过const对象和const成员函数来使用Time类:figl7_01.cpp

।"良好的编程习惯17.1________________________________________________________

|^|将所有不需要修改当前对象的成员函数声明为const,这样,如果需要,可以在

const对象上使用它们。

注意,即使构造函数必须是非const成员函数,它仍然为const对象调用。第42行和43

行的Time构造函数的定义:

Time::Time(inthr,intmin,intsec)

{setTime(hr,min,sec);}

说明Time构造函数调用了非const成员函数setTime以执行Time对象的初始化。对于const

对象,允许从构造函数调用中调用非const成员函数。对象的const特性是在构造函数完成对

象的初始化到调用该对象的析构函数期间强制执行的。

软件工程经验17.4__________________________________________________________

不能通过赋值来修改const对象,所以,该对象必须初始化。当类的数据成员声

明为const时,则使用成员初始值来为构造函数提供类对象的数据成员的初始值。

还要注意,第108行(源文件中的第20行):

noon.printstandard();//non-constconst

产生了编译程序错误,即使类Time的成员函数printstandard没有修改调用它的对象。没有修

改对象并不足以说明它是const方法。该方法必须也明确声明为const。

图17.2说明使用成员初始值来初始化类Increment的const数据成员increment«Increment

的构造函数将修改如下:

1//Fig.17.2:figl7_02.cpp

2//Usingamemberinitializertoinitializea

3//constantofabuilt-indatatype.

4#include<iostream>

5

6usingstd::cout;

7usingstd::endl;

8

9classIncrement{

10public:

11Increment(intc=0,inti=1);

12voidaddlncrement(){count+=increment;}

13voidprint()const;

14

15private:

16intcount;

17constintincrement;//constdatamember

18};

19

20//ConstructorforclassIncrement

548第2部分C++编程

21Increment::Increment(intc,inti)

22:increment(i)//initializerforconstmember

23{count=c;}

24

25//Printthedata

26voidIncrement::print()const

27(

28cout<<11count="<<count

29<<”,increment=*'<<increment<<endl;

30}

31

32intmain()

33(

34Incrementvalue(10,5);

35

36cout<<"Beforeincrementing:f,;

37value.print();

38

39for(intj=0;j<3;j++){

40value.addincrement();

41cout<<"Afterincrementn<<j+1<<*':";

42value.print();

43)

44

45return0;

46}

Beforeincrementing:count=10,increment=5

Afterincrement1:count=15,increment=5

Afterincrement2:count=20,increment=5

Afterincrement3:count-25,increment=5

图17.2使用成员初始值来初始化内置数据类型的常量

Increment::Increment(intc,inti)

:increment(i)

{count=c;}

符号:increment⑴将increment初始化为值i,如果需要多个初始值,则只需将它们包含在

冒号后面用逗号分开的列表中。可以用成员初始值语法来初始化所有数据成员,但必须用这种

方式来初始化const和引用。本章的后面,我们将看见必须用这种方式初始化成员对象。在第

19章中,当我们研究继承时,将看见派生类的基类部分也必须用这种方式初始化。

测试和调试提示17.1

如果函数不修改对象,则总是声明成员函数为const。这可以帮助避免许多错误。

图17.3说明了两个流行C++编译程序为尝试用赋值语句,而不是用成员初始值来初始化

increment的程序所产生的两个编译程序错误。

1//Fig.17.3:figl7_03.cpp

2//Attemptingtoinitializeaconstantof

3//abuilt-indatatypewithanassignment.

4#include<iostream>

5

6usingstd::cout;

7usingstd::endl;

8

9classIncrement{

10public:

11Increment(intc=0,inti=1);

12voidaddincrement(){count+=increment;}

13voidprint()const;

14private:

15intcount;

16constintincrement;

17};

18

19//ConstructorforclassIncrement

20Increment::Increment(intc,inti)

21{//Constantmember*increment'isnotinitialized

22count=c;

23increment=i;//ERROR:Cannotmodifyaconstobject

24}

25

26//Printthedata

27voidIncrement::print()const

28(

29cout<<"count=n<<count

30<<”,increment=*'<<increment<<endl;

31}

32

33intmain()

34(

35Incrementvalue(10,5);

36

37cout<<"Beforeincrementing:**;

38value.print();

39

40for(intj=0;j<3;j++){

41value.addlncrement();

42cout<<"Afterincrement"<<j<<":11;

43value.print();

550第2部分C++编程

44}

45

46return0;

47}

BorlandC++命令行编译程序警告和错误消息

Figl7__03.cpp:

WarningW8038Figl7_03.cpp21:Constantmember'Increment::increment,

isnotinitializedinfunctionIncrement::Increment(int,int)

ErrorE2024Figl7_03.cpp23:Cannotmodifyaconstobjectinfunction

Increment::Increment(int,int)

WarningW8057Figl7_03.cpp24:Parameter'i'isneverusedinfunction

Increment::Increment(int,int)

***1errorsinCompile***

MicrosoftVisualC++编译程序错误消息

Compiling...

Figl7_03,cpp

D:\Figl7_03.cpp(21):errorC2758:'incrementf:mustbeinitializedin

constructorbase/memberinitializerlist

D:\Figl7_03.cpp(16):seedeclarationof*increment'

D:\Figl7_03.cpp(23):errorC2166:1-valuespecifiesconstobject

Errorexecutingcl.exe.

test.exe-2error(s),0warning(s)

图17.3尝试通过赋值语句来初始化内置数据类型常量是错误的

7^3常见的编程错误17.5_________________________________________________________

没有为const数据成员提供成员初始值将导致语法错误。

软件工程经验17.5__________________________________________________________

常量类成员(const对象和const"变量")必须用成员初始值语法来初始化;不

允许赋值。

注意,第27行的print函数声明为const<>因为我们可能永远不会有constincrement对象,

因此将这个函数标记为const是合理的,但却是奇怪的。

软件工程经验17.6__________________________________________________________

将不修改所操作对象的类的所有成员函数声明为const是好的习惯。某些情况下,

因为并不希望创建那个类的const对象,因此这可能是反常的。声明这样的成员

函数为const确实有好处。如果在那个成员函数中无意修改了对象,则编译程序

将发出语法错误消息。

测试和调试提示17.2

类似C++这样的语言,随着语言的发展,其目标也会变化。在语言中很可能添加更

多的关键字。避免使用类似“object”这样的单词作为标识符.即使“object”

当前并不是C++中的关键字,它也可能成为关键字,所以将来用新的编译程序来编

译时,可能现有的代码会不符合要求。

17.3复合:作为类成员的对象

AlarmClock类对象需要知道何时发出警告,所以为什么不包含Time对象作为

AlarmClock对象的一个成员?这样的功能称为复合(composition)。类可以用其他类的对

象作为成员。

软件工程经验17.7____________________________________________________

软件重用性的最常见形式就是复合,其中类用其他类的对象作为成员。

当创建对象时,将自动调用它的构造函数,所以我们需要指定向成员对象构造函数传递多

少个参数。应该按照声明对象的顺序来构造成员对象(不是按照构造函数成员初始值列表中列

出的顺序),而且在包含它们的类对象(有时候称为宿主对象)被构造之前构造。

图17.4使用类Employee和类Date来说明某个对象作为其他对象的成员。类Employee包

含private数据成员firstName、lastName、birthDate和hireDate。成员birthDate和hireDate是

类date的const对象,它们包含private数据成员month、day和year。该程序实例化Employee

对象,并初始化和显示它的数据成员。注意Employee构造函数定义中的函数头的语法:

Employee::Employee(char*fname,char*lname,

intbmonth,intbday,intbyear,

inthmonth,inthday,inthyear)

:birthDate(bmonth,bday,byear),

hireDate(hmonth,hday,hyear)

1//Fig.17.4:datel.h

2//DeclarationoftheDateclass.

3//Memberfunctionsdefinedindatel.cpp

4#ifndefDATE1_H

5#defineDATE1_H

6

7classDate{

8public:

9Date(int=1,int=1,int=1900);//defaultconstructor

10voidprint()const;//printdateinmonth/day/yearformat

11-Date();//providedtoconfirmdestructionorder

12private:

13intmonth;//1-12

14intday;//1-31basedonmonth

15intyear;//anyyear

552第2部分C++编程

1&

17//utilityfunctiontotestproperdayformonthandyear

18intcheckDay(int);

19);

20

21#endif

图17.4使用成员对象初始值:datel.h

22//Fig.17.4:datel.cpp

23//MemberfunctiondefinitionsforDateclass.

24#include<iostream>

25

26usingstd::cout;

27usingstd::endl;

28

29#include"datel.h”

30

31//Constructor:Confirmpropervalueformonth;

32//callutilityfunctioncheckDaytoconfirmproper

33//valueforday.

34Date::Date(intmn,intdy,intyr)

35(

36if(mn>0&&mn<=12)//validatethemonth

37month=mn;

38else(

39month=1;

40cout<<"Month”<<mn<<'*invalid.Settomonth1.\n;

41}

42

43year=yr;//shouldvalidateyr

44day=checkDay(dy);//validatetheday

45

46cout<<"Dateobjectconstructorfordate";

47print();//interesting:aprintwithnoarguments

48cout<<endl;

49)

50

51//PrintDateobjectinformmonth/day/year

52voidDate::print()const

53{cout<<month<<*/'<<day<<*/*<<year;}

54

55//Destructor:providedtoconfirmdestructionorder

56Date::~Date()

57(

58cout<<"Dateobjectdestructorfordate

59print();

60cout<<endl;

61

62

63//Utilityfunctiontoconfirmproperdayvalue

64//basedonmonthandyear.

65//Istheyear2000aleapyear?

66intDate::checkDay(inttestDay)

67(

68staticconstintdaysPerMonth[13]=

69{0,31,28,31,30,31,30,31,31,30,31,30,31);

70

71if(testDay>0&&testDay<=daysPerMonth[month])

72returntestDay;

73

74if(month==2&&//February:Checkforleapyear

75testDay==29&&

76(year%400==0II

77(year%4==0&&year%100!=0)))

78returntestDay;

79

80cout<<"Day"<<testDay<<”invalid.Settoday1.\n";

81

82return1;//leaveobjectinconsistentstateifbadvalue

83

图17.4使用成员对象初始值:datel.cpp

84//Fig.17.4:emply1.h

85//DeclarationoftheEmployeeclass.

86//Memberfunctionsdefinedinemply1.cpp

87#ifndefEMPLY1_H

88#defineEMPLY1_H

89

90#include"datel.hn

91

92classEmployee{

93public:

94Employee(char*,char*,int,int,int,int,int,int);

95voidprint()const;

96-Employee();//providedtoconfirmdestructionorder

97private:

98charfirstName[25];

99charlastName[25];

100constDatebirthDate;

101constDatehireDate;

102};

103

104#endif

图17.4使用成员对象初始值:emplyl.h

554第2部分C++编程

105//Fig.17.4:emplyl.cpp

106//MemberfunctiondefinitionsforEmployeeclass.

107#include<iostream>

108

109usingstd::cout;

110usingstd::endl;

111

112#include<cstring>

113#include"emplyl.h”

114#include"datel.hu

115

116Employee::Employee(char*fname,char*lname,

117intbmonth,intbdayzintbyear,

118inthmonth,inthday,inthyear)

119:birthDate(bmonth,bday,byear),

120hireDate(hmonth,hdayzhyear)

121(

122//copyfnameintofirstNameandbesurethatitfits

123intlength=strlen(fname);

124length=(length<25?length:24);

125strncpy(firstName,fname,length);

126firstName[length]=1\0*;

127

128//copyInameintolastNameandbesurethatitfits

129length=strlen(Iname);

130length=(length<25?length:24);

131strncpy(lastName,Iname,length);

132lastName[length]=*\0';

133

134cout<<"Employeeobjectconstructor:"

135<<firstName<<'*<<lastName<<endl;

136

137

138voidEmployee::print()const

139(

140cout<<lastName<<","<<firstName<<*'\nHired:

141hireDate.print();

142cout<<”Birthdate:n;

143birthDate.print();

144cout<<endl;

145}

146

147//Destructor:providedtoconfirmdestructionorder

148Employee::^Employee()

149(

150cout<<"Employeeobjectdestructor:"

151<<lastName<<","<<firstName<<endl;

152}

图17.4使用成员对象初始值:emplyl.cpp

153//Fig.17.4:figl7_04.cpp

154//Demonstratingcomposition:anobjectwithmemberobjects.

155#include<iostream>

156

157usingstd::cout;

158usingstd::endl;

159

160#includenemplyl.h"

161

162intmain()

163(

164Employeee(“Bob”,“Jones”,7,24z1949,3,12,1988);

165

166cout<<'\n*;

167e.print();

168

169cout<<n\nTestDateconstructorwithinvalidvalues:\nH;

170Dated(14,35,1994);//invalidDatevalues

171cout<<endl;

172return0;

173}

Dateobjectconstructorfordate7/24/1949

Dateobjectconstructorfordate3/12/1988

Employeeobjectconstructor:BobJones

Jones,Bob

Hired:3/12/1988Birthdate:7/24/1949

TestDateConstructorwithinvalidvalues:

Month14invalid.Settomonth1.

Day35invalid.Settoday1.

Dateobjectconstructorfordate1/1/1994

Dateobjectdestructorfordate1/1/1994

Employeeobjectdestructor:Jones,Bob

Dateobjectdestructorfordate3/12/1988

Dateobjectdestructorfordate7/24/1949

17.4使用成员对象初始值:figl7_04.cpp

Employee构造函数有8个参数(fname、Iname、bmonth、bday、byear、hmonth>hday和

hyear),头部中的冒号(:)分开了成员初始值和参数列表。成员初始值指定Employee参数传

556第2部分C++编程

递给成员Date对象的构造函数。参数bmonth、bday和byear传递给对象birthDate的构造函数,

参数hmonth、hday和hyear传递给对象hireDate的构造函数。用逗号分开多个成员初始值。

记住,const成员和引用也是在成员初始值列表中初始化的(在第19章中,我们将看见派

生类的基类部分也用这种方式初始化)。类date和类Employee都包含析构函数,当破坏Date

对象或者Employee对象时,它们将分别打印消息。这使得我们可以在程序输出中确信对象是

从内向外构造的,而且从外向内按照相反的顺序来破坏(也就是说,Date成员对象在包含它

们的Employee对象破坏之后破坏)。

成员对象并不需要通过成员初始值来明确初始化。如果没有提供成员初始值,则将隐含调

用成员对象的默认构造函数。如果默认构造函数创建了值,则用set函数来覆盖。然而,对于

复杂的初始化,这种方法需要相当多的额外工作和时间。

7^x1常见的编程错误17.6________________________________________________________

当没有为成员对象提供成员初始值时,没有为成员对象类提供默认构造函数将导

致语法错误。

性能提示17.2______________________________________________________________

@用成员初始值来明确地初始化成员对象。这避免了“双重初始化”成员对象的开

销:在调用成员对象的默认构造函数时初始化一次,在

温馨提示

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

评论

0/150

提交评论