面向对象程序设计(C++):第6章 数组指针与字符串

上传人:大米 文档编号:568761429 上传时间:2024-07-26 格式:PPT 页数:144 大小:1.54MB
返回 下载 相关 举报
面向对象程序设计(C++):第6章 数组指针与字符串_第1页
第1页 / 共144页
面向对象程序设计(C++):第6章 数组指针与字符串_第2页
第2页 / 共144页
面向对象程序设计(C++):第6章 数组指针与字符串_第3页
第3页 / 共144页
面向对象程序设计(C++):第6章 数组指针与字符串_第4页
第4页 / 共144页
面向对象程序设计(C++):第6章 数组指针与字符串_第5页
第5页 / 共144页
点击查看更多>>
资源描述

《面向对象程序设计(C++):第6章 数组指针与字符串》由会员分享,可在线阅读,更多相关《面向对象程序设计(C++):第6章 数组指针与字符串(144页珍藏版)》请在金锄头文库上搜索。

1、C+语言程序设计第六章 数组 指针与字符串目录6.1 数组6.2 指针6.3 动态内存分配6.4 用vector创建数组对象6.5 深拷贝与浅拷贝6.6 字符串6.7 综合实例个人银行账户管理程序6.8 深度探索6.9 小结2数组的概念数组是具有一定顺序关系的若干相同类型变量的集合体,组成数组的变量称为该数组的元素。数组属于构造类型。36.1 数组6.1.1 数组的声明与使用数组的声明46.1 数组类型说明符 数组名 常量表达式 常量表达式 ;数组名的构成方法与一般变量名相同。例如:int a10; 表示a为整型数组,有10个元素:a0.a9例如: int a53;表示a为整型二维数组,其中第

2、一维有5个下标(04),第二维有3个下标(02),数组的元素个数为15,可以用于存放5行3列的整型数据表格。6.1.1 数组的声明与使用(续)引用必须先声明,后使用。只能逐个引用数组元素,而不能一次引用整个数组例如:a0=a5+a7-a2*3例如:b12=a23/256.1 数组66.1 数组 6.1.1 数组的声明与使用例6-1#includeusingnamespacestd;intmain()inta10,b10;for(inti=0;i10;i+)ai=i*2-1;b10-i-1=ai;for(inti=0;i10;i+)coutai=ai;coutbI=biendl;return0;

3、76.1 数组6.1.2 数组的存储与初始化一维数组的存储数组元素在内存中顺次存放,它们的地址是连续的。例如:a0a1a2a3a4a5a6a7a8a9a数组名字是数组首元素的内存地址。数组名是一个常量,不能被赋值。86.1 数组6.1.2 数组的存储与初始化(续)一维数组的初始化可以在定义数组的同时赋给初值:在声明数组时对数组元素赋以初值。例如:static int a10=0,1,2,3,4,5,6,7,8,9;可以只给一部分元素赋初值。例如:static int a10=0,1,2,3,4;在对全部数组元素赋初值时,可以不指定数组长度。例如:static int a=0,1,2,3,4,5

4、,6,7,8,996.1 数组6.1.2 数组的存储与初始化(续)二维数组的存储按行存放例如: float a34;其中数组a的存储顺序为: a0a00 a01 a02 a03a1a10 a11 a12 a13a2a20 a21 a22 a23a可以理解为:a00 a01 a02 a03 a10 a11 a12 a13 a20 a21 a22 a23106.1 数组6.1.2 数组的存储与初始化(续)二维数组的初始化将所有数据写在一个内,按顺序赋值例如:static int a34=1,2,3,4,5,6,7,8,9,10,11,12;分行给二维数组赋初值例如:static int a34 =

5、1,2,3,4,5,6,7,8,9,10,11,12;可以对部分元素赋初值例如:static int a34=1,0,6,0,0,11;116.1 数组 6.1.2 数组的存储与初始化例: 数组处理求Fibonacci数列#includeusingnamespacestd;intmain()intf20=1,1;/初始化第0、1个数for(inti=2;i20;i+)/求第219个数fi=fi-2+fi-1;for(i=0;i20;i+)/输出,每行5个数if(i%5=0)coutendl;cout.width(12);/设置输出宽度为12coutfi;return0;126.1 数组 6.1

6、.2 数组的存储与初始化例 (续)运行结果:运行结果:1 11 12 23 35 58 81313212134345555898914414423323337737761061098798715971597258425844181418167656765例: 一维数组应用举例循环从键盘读入若干组选择题答案,计算并输出每组答案的正确率,直到输入ctrl+z为止。每组连续输入5个答案,每个答案可以是a.d。136.1 数组 6.1.2 数组的存储与初始化例 (续)#includeusingnamespacestd;intmain()constcharKEY=a,c,b,a,d;constintNU

7、M_QUES=5;charc;intques=0,numCorrect=0;coutEntertheNUM_QUESquestiontests:endl;while(cin.get(c)if(c!=n)if(c=keyques)numCorrect+;cout;elsecout*;ques+;elsecoutScorestatic_cast(numCorrect)/NUM_QUES*100%;ques=0;numCorrect=0;coutendl;return0;146.1 数组 6.1.2 数组的存储与初始化156.1 数组 6.1.2 数组的存储与初始化例 (续)运行结果:acbba*S

8、core60%acbadScore100%abbda*Score40%bdcba*Score0%166.1 数组6.1.3 数组作为函数参数数组元素作实参,与单个变量一样。数组名作参数,形、实参数都应是数组名,类型要一样,传送的是数组首地址。对形参数组的改变会直接影响到实参数组。例6-2 使用数组名作为函数参数主函数中初始化一个矩阵并将每个元素都输出,然后调用子函数,分别计算每一行的元素之和,将和直接存放在每行的第一个元素中,返回主函数之后输出各行元素的和。176.1 数组 6.1.3 数组作为函数参数#includeusingnamespacestd;voidrowSum(inta4,int

9、nRow)for(inti=0;inRow;i+)for(intj=1;j4;j+)ai0+=aij;intmain()/主函数inttable34=1,2,3,4,2,3,4,5,3,4,5,6;/声明并初始化数组18例6-2 (续)6.1 数组 6.1.3 数组作为函数参数/输出数组元素for(inti=0;i3;i+)for(intj=0;j4;j+)couttableij;coutendl;rowSum(table,3);/调用子函数,计算各行和/输出计算结果for(inti=0;i3;i+)coutSumofrowiistablei0endl;return0;19例6-2 (续)6.

10、1 数组 6.1.3 数组作为函数参数20例6-2 (续)运行结果:123423453456Sumofrow0is10Sumofrow1is14Sumofrow2is186.1 数组 6.1.3 数组作为函数参数216.1 数组6.1.4 对象数组声明:类名 数组名元素个数;访问方法:通过下标访问 数组名下标.成员名22对象数组初始化数组中每一个元素对象被创建时,系统都会调用类构造函数初始化该对象。通过初始化列表赋值。 例:Point a2=Point(1,2),Point(3,4);如果没有为数组元素指定显式初始值,数组元素便使用默认值初始化(调用缺省构造函数)。6.1 数组 6.1.4 对

11、象数组23数组元素所属类的构造函数不声明构造函数,则采用缺省构造函数。各元素对象的初值要求为相同的值时,可以声明具有默认形参值的构造函数。各元素对象的初值要求为不同的值时,需要声明带形参的构造函数。当数组中每一个对象被删除时,系统都要调用一次析构函数。6.1 数组 6.1.4 对象数组24例6-3 对象数组应用举例/Point.h#ifndef_POINT_H#define_POINT_HclassPoint/类的定义public:/外部接口Point();Point(intx,inty);Point();voidmove(intnewX,intnewY);intgetX()constretu

12、rnx;intgetY()constreturny;staticvoidshowCount();/静态函数成员private:/私有数据成员intx,y;#endif/_POINT_H6.1 数组 6.1.4 对象数组25例6-3 (续)/Point.cpp#include#includePoint.husingnamespacestd;Point:Point()x=y=0;coutDefaultConstructorcalled.endl;Point:Point(intx,inty):x(x),y(y)coutConstructorcalled.endl;Point:Point()coutD

13、estructorcalled.endl;voidPoint:move(intnewX,intnewY)coutMovingthepointto(newX,newY)endl;x=newX;y=newY;6.1 数组 6.1.4 对象数组26例6-3 (续)/6-3.cpp#includePoint.h#includeusingnamespacestd;intmain()coutEnteringmain.endl;Pointa2;for(inti=0;i2;i+)ai.move(i+10,i+20);coutExitingmain.endl;return0;6.1 数组 6.1.4 对象数组2

14、7例6-3 (续)运行结果:Enteringmain.DefaultConstructorcalled.DefaultConstructorcalled.Movingthepointto(10,20)Movingthepointto(11,21)Exitingmain.Destructorcalled.Destructorcalled.6.1 数组 6.1.4 对象数组6.2.1 内存空间的访问方式内存空间的访问方式通过变量名访问通过地址访问336.2 指针6.2.2 指针变量的声明概念指针:指针:内存地址,用于间接访问内存单元指针变量:指针变量:用于存放地址的变量声明例:static int

15、 i;static int *ptr = &i;引用例1:i = 3;例2:*ptr = 3;346.2 指针指向整型变量的指针20003ptr*ptri2000内存用户数据区变量 i变量 j变量 ptr3620002000200430106.2.3 与地址相关的运算“*”和“&”地址运算符:&例:int var;则 &var 表示变量 var 在内存中的起始地址356.2 指针6.2.4 指针的赋值指针变量的初始化语法形式存储类型 数据类型 *指针名初始地址;例:int *pa = &a;注意事项用变量地址作为初值时,该变量必须在指针初始化之前已声明过,且变量类型应与指针类型一致。可以用一个

16、已赋初值的指针去初始化另一个指针变量。不要用一个内部 auto 变量去初始化 static 指针。366.2 指针指针变量的赋值运算指针名指针名= =地址地址“地址”中存放的数据类型与指针类型必须相符。向指针变量赋的值必须是地址常量或变量,不能是普通整数。但可以赋值为整数0,表示空指针。指针的类型是它所指向变量的类型,而不是指针本身数据值的类型,任何一个指针本身的数据值都是unsigned long int型。允许声明指向 void 类型的指针。该指针可以被赋予任何类型对象的地址。例: void *general; 376.2 指针 6.2.4 指针的赋值例6-5 指针的声明、赋值与使用#in

17、cludeusingnamespacestd;intmain()inti;/定义int型数iint*ptr=&i;/取i的地址赋给ptri=10;/int型数赋初值couti=iendl;/输出int型数的值cout*ptr=*ptrendl;/输出int型指针所指地址的内容return0;386.2 指针 6.2.4 指针的赋值运行结果:i=10*ptr=10例6-6 void类型指针的使用#includeusingnamespacestd;intmain()/!voidvoidObject;错,不能声明void类型的变量void*pv;/对,可以声明void类型的指针inti=5;pv=&

18、i;/void类型指针指向整型变量int*pint=static_cast(pv);/void类型指针赋值给int类型指针cout*pint=*pintendl;return0;396.2 指针 6.2.4 指针的赋值指向常量的指针不能通过指针来改变所指对象的值,但指针本身可以改变,可以指向另外的对象。例:int a;const int *p1 = &a;/p1是指向常量的指针int b;p1 = &b;/正确,p1本身的值可以改变*p1 = 1;/编译时出错,不能通过p1改变所指的对象406.2 指针 6.2.4 指针的赋值指针类型的常量若声明指针常量,则指针本身的值不能被改变。例:int

19、a;int * const p2 = &a; p2 = &b;/错误,p2是指针常量,值不能改变416.2 指针 6.2.4 指针的赋值指针变量的算术运算指针与整数的加减运算指针p加上或减去n,其意义是指针当前指向位置的前方或后方第n个数据的地址。这种运算的结果值取决于指针指向的数据类型。p1n1等价于*(p1 + n1)指针加一,减一运算指向下一个或前一个数据。例如:y=*px+ 相当于 y=*(px+) (*和+优先级相同,自右向左运算)426.2 指针 6.2.5 指针运算papa-2pa-1pa+1pa+2pa+3*(pa-2)或pa-2*pa或pa0*(pa+1)或pa1*(pa+2

20、)或pa2*(pa+3)或pa3*(pa-1)或pa-1short*pa436.2 指针 6.2.5 指针运算pb-1pbpb+1pb+2*(pb-1)或pb-1*pb或pb0*(pb+1)或pb1*(pb+2)或pb2long*pb446.2 指针 6.2.5 指针运算指针变量的关系运算关系运算指向相同类型数据的指针之间可以进行各种关系运算。指向不同数据类型的指针,以及指针与一般整数变量之间的关系运算是无意义的。指针可以和零之间进行等于或不等于的关系运算。例如:p=0或p!=0赋值运算向指针变量赋的值必须是地址常量或变量,不能是普通整数。但可以赋值为整数0,表示空指针。456.2 指针 6.

21、2.5 指针运算6.2.6 用指针处理数组元素声明与赋值例:int a10, *pa; pa=&a0; 或 pa=a;通过指针引用数组元素经过上述声明及赋值后:*pa就是a0,*(pa+1)就是a1,. ,*(pa+i)就是ai.ai, *(pa+i), *(a+i), pai都是等效的。不能写 a+,因为a是数组首地址是常量。466.2 指针例6-7476.2 指针 6.2.6 用指针处理数组元素设有一个int型数组a,有10个元素。用三种方法输出各元素:使用数组名和下标使用数组名和指针运算使用指针变量例6-7 (续) 使用数组名和下标486.2 指针 6.2.6 用指针处理数组元素#inc

22、ludeusingnamespacestd;intmain()inta10=1,2,3,4,5,6,7,8,9,0;for(inti=0;i10;i+)coutai;coutendl;return0;例6-7 (续) 使用数组名指针运算496.2 指针 6.2.6 用指针处理数组元素#includeusingnamespacestd;intmain()inta10=1,2,3,4,5,6,7,8,9,0;for(inti=0;i10;i+)cout*(a+i);coutendl;return0;例6-7 (续) 使用指针变量506.2 指针 6.2.6 用指针处理数组元素#includeusi

23、ngnamespacestd;intmain()inta10=1,2,3,4,5,6,7,8,9,0;for(int*p=a;p(a+10);p+)cout*p;coutendl;return0;6.2.7 指针数组数组的元素是指针型例:Point *pa2;由pa0,pa1两个指针组成 516.2 指针例6-8 利用指针数组存放单位矩阵526.2 指针 6.2.7 指针数组#includeusingnamespacestd;intmain()intline1=1,0,0;/矩阵的第一行intline2=0,1,0;/矩阵的第二行intline3=0,0,1;/矩阵的第三行/定义整型指针数组并

24、初始化int*pLine3=line1,line2,line3;例6-8 (续)536.2 指针 6.2.7 指针数组coutMatrixtest:endl;/输出单位矩阵for(inti=0;i3;i+)for(intj=0;j3;j+)coutpLineij;coutendl;return0;输出结果为:Matrixtest:1,0,00,1,00,0,1例6-9 二维数组举例546.2 指针 6.2.7 指针数组#includeusingnamespacestd;intmain()intarray233=11,12,13,21,22,23,31,32,33;for(inti=0;i3;i

25、+)for(intj=0;j3;j+)cout*(*(array2+i)+j);/逐个输出二维数组第i行元素值coutendl;return0;55例6-9 (续)运行结果:1112132122233132336.2 指针 6.2.7 指针数组56指针数组 vs 二维数组6.2 指针 6.2.7 指针数组pLine0 pLine1 pLine2(a) 指针数组array20array21array22array2二维数组pLine6.2.8 用指针作为函数参数以地址方式传递数据,可以用来返回函数处理结果。实参是数组名时形参可以是指针。576.2 指针例6-10586.2 指针 6.2.8 用指

26、针作为函数参数题目:读入三个浮点数,将整数部分和小数部分分别输出#includeusingnamespacestd;voidsplitFloat(floatx,int*intPart,float*fracPart)/取x的整数部分*intPart=static_cast(x);/取x的小数部分*fracPart=x-*intPart;例6-10 (续)596.2 指针 6.2.8 用指针作为函数参数intmain()coutEnter3floatpointnumbers:endl;for(inti=0;ix;splitFloat(x,&n,&f);/变量地址作为实参coutIntegerPar

27、t=nFractionPart=fendl;return0;60例6-10 (续)运行结果:Enter3floatingpointnumbers4.7IntegerPart=4FractionPart=0.78.913IntegerPart=8FractionPart=0.913-4.7518IntegerPart=-4FractionPart=-0.75186.2 指针 6.2.8 用指针作为函数参数例: 输出数组元素的内容和地址616.2 指针 6.2.8 用指针作为函数参数#include#includeusingnamespacestd;voidarrayPtr(long*p,intn

28、)coutInfunc,addressofarrayispendl;coutAccessingarrayusingpointersendl;for(inti=0;in;i+)coutAddressforindexiisp+i;coutValueis*(p+i)endl;例 (续)626.2 指针 6.2.8 用指针作为函数参数intmain()longlist5=50,60,70,80,90;coutInmain,addressofarrayislistendl;coutendl;arrayPtr(list,5);return0;63例 (续)运行结果:Inmain,addressofarra

29、yis0012FF50Infunc,addressofarrayis0012FF50AccessingarrayusingpointersAddressforindex0is0012FF50Valueis50Addressforindex1is0012FF54Valueis60Addressforindex2is0012FF58Valueis70Addressforindex3is0012FF5CValueis80Addressforindex4is0012FF60Valueis906.2 指针 6.2.8 用指针作为函数参数例: 指向常量的指针做形参646.2 指针 6.2.8 用指针作为函

30、数参数#includeusingnamespacestd;constintN=6;voidprint(constint*p,intn);intmain()intarrayN;for(inti=0;iarrayi;print(array,N);return0;voidprint(constint*p,intn)cout*p;for(inti=1;in;i+)cout,*(p+i);coutendl;6.2.9 指针型函数当函数的返回值是地址时,该函数就是指针形函数。声明形式存储类型 数据类型 *函数名()656.2 指针6.2.10 指向函数的指针声明形式存储类型 数据类型 (*函数指针名)()

31、;含义:数据指针指向数据存储区,而函数指针指向的是程序代码存储区。666.2 指针例6-11 函数指针676.2 指针 6.2.10 指向函数的指针#includeusingnamespacestd;voidprintStuff(float)coutThisistheprintstufffunction.endl;voidprintMessage(floatdata)coutThedatatobelistedisdataendl;voidprintFloat(floatdata)coutThedatatobeprintedisdata成员名ptr-getx() 相当于 (*ptr).getx(

32、);706.2 指针例6-12使用指针来访问Point类的成员/6_12.cpp#includeusingnamespacestd;classPoint/类的定义public:/外部接口Point(intx=0,inty=0):x(x),y(y)/构造函数intgetX()constreturnx;/返回xintgetY()constreturny;/返回yprivate:/私有数据intx,y;716.2 指针 6.2.11 对象指针的一般概念例6-12(续)72intmain()/主函数Pointa(4,5);/定义并初始化对象aPoint*p1=&a;/定义对象指针,用a的地址将其初始化

33、coutgetX()endl;/利用指针访问对象成员couta.getX()x;766.2 指针 6.2.11 对象指针指向类的非静态成员的指针通过指向成员的指针只能访问公有成员声明指向成员的指针声明指向公有数据成员的指针类型说明符 类名:*指针名;声明指向公有函数成员的指针类型说明符 (类名:*指针名)(参数表);776.2 指针 6.2.11 对象指针指向类的非静态成员的指针(续)指向数据成员的指针说明指针应该指向哪个成员指针名 = &类名:数据成员名;通过对象名(或对象指针)与成员指针结合来访问数据成员对象名.*类成员指针名或: 对象指针名-*类成员指针名786.2 指针 6.2.11

34、对象指针指向类的非静态成员的指针(续)指向函数成员的指针初始化指针名=&类名:函数成员名;通过对象名(或对象指针)与成员指针结合来访问函数成员(对象名.*类成员指针名)(参数表)或:(对象指针名-*类成员指针名)(参数表)796.2 指针 6.2.11 对象指针例6-13 访问对象的公有成员函数的不同方式80intmain()/主函数 Pointa(4,5);/声明对象APoint*p1=&a;/声明对象指针并初始化/声明成员函数指针并初始化int(Point:*funcPtr)()=Point:getX;/(1)使用成员函数指针访问成员函数cout(a.*funcPtr)()endl;/(2

35、)使用成员函数指针和对象指针访问成员函数cout*funcPtr)()endl;/(3)使用对象名访问成员函数couta.getX()endl;/(4)使用对象指针访问成员函数coutgetX()endl;return0;6.2 指针 6.2.11 对象指针指向类的静态成员的指针对类的静态成员的访问不依赖于对象可以用普通的指针来指向和访问静态成员例6-14通过指针访问类的静态数据成员例6-15通过指针访问类的静态函数成员816.2 指针 6.2.11 对象指针例6-14 通过指针访问类的静态数据成员82#includeusingnamespacestd;classPoint /Point类定义

36、public:/外部接口Point(intx=0,inty=0):x(x),y(y)count+;Point(constPoint&p):x(p.x),y(p.y)count+;Point()count-;intgetX()constreturnx;intgetY()constreturny;staticintcount;private:/私有数据成员intx,y;intPoint:count=0;6.2 指针 6.2.11 对象指针例6-14 (续)83intmain()/主函数实现/定义一个int型指针,指向类的静态成员int*ptr=&Point:count;Pointa(4,5);/定

37、义对象acoutPointA:a.getX(),a.getY();coutObjectcount=*ptrendl;Pointb(a);/定义对象bcoutPointB:b.getX(),b.getY();coutObjectcount=*ptrendl;return0;6.2 指针 6.2.11 对象指针例6-15 通过指针访问类的静态函数成员84#includeusingnamespacestd;classPoint/Point类定义public:/外部接口Point(intx=0,inty=0):x(x),y(y)count+;Point(constPoint&p):x(p.x),y(p

38、.y)count+;Point()count-;intgetX()constreturnx;intgetY()constreturny;staticvoidshowCount()coutObjectcount=countendl;private:/私有数据成员intx,y;staticintcount;intPoint:count=0;6.2 指针 6.2.11 对象指针例6-15 (续)85intmain()/主函数实现/定义一个指向函数的指针,指向类的静态成员函数void(*funcPtr)()=Point:showCount;Pointa(4,5);/定义对象AcoutPointA:a.

39、getX(),a.getY();funcPtr(); /输出对象个数,直接通过指针访问静态函数成员Pointb(a);/定义对象BcoutPointB:b.getX(),b.getY();funcPtr(); /输出对象个数,直接通过指针访问静态函数成员return0;6.2 指针 6.2.11 对象指针866.3 动态内存分配动态申请内存操作符 newnew 类型名T(初始化参数列表)功能:在程序执行期间,申请用于存放T类型对象的内存空间,并依初值列表赋以初值。结果值:成功:T类型的指针,指向新分配的内存;失败:抛出异常。876.3 动态内存分配释放内存操作符deletedelete 指针p

40、功能:释放指针p所指向的内存。p必须是new操作的返回值。例6-16 动态创建对象举例88#includeusingnamespacestd;classPointpublic:Point():x(0),y(0)coutDefaultConstructorcalled.endl;Point(intx,inty):x(x),y(y)coutConstructorcalled.endl;Point()coutDestructorcalled.endl;intgetX()constreturnx;intgetY()constreturny;voidmove(intnewX,intnewY)x=newX

41、;y=newY;private:intx,y;6.3 动态内存分配例6-16 (续)89intmain()coutStepone:endl;Point*ptr1=newPoint;/调用缺省构造函数deleteptr1;/删除对象,自动调用析构函数coutSteptwo:endl;ptr1=newPoint(1,2);deleteptr1;return0;6.3 动态内存分配运行结果:运行结果:StepOne:DefaultConstructorcalled.Destructorcalled.StepTwo:Constructorcalled.Destructorcalled.906.3 动态

42、内存分配申请和释放动态数组分配:new 类型名T 数组长度 数组长度可以是任何表达式,在运行时计算释放:delete 数组名p释放指针p所指向的数组。p必须是用new分配得到的数组首地址。例6-17 动态创建对象数组举例91#includeusingnamespacestd;classPoint/类的声明同例6-16,略;intmain()Point*ptr=newPoint2;/创建对象数组ptr0.move(5,10);/通过指针访问数组元素的成员ptr1.move(15,20);/通过指针访问数组元素的成员coutDeleting.endl;deleteptr;/删除整个对象数组retu

43、rn0;6.3 动态内存分配92例6-17 (续)运行结果:DefaultConstructorcalled.DefaultConstructorcalled.Deleting.Destructorcalled.Destructorcalled.6.3 动态内存分配936.3 动态内存分配将动态数组封装成类更加简洁,便于管理建立和删除数组的过程比较繁琐封装成类后更加简洁,便于管理可以在访问数组元素前检查下标是否越界用assert来检查,assert只在调试时生效例6-18 动态数组类94#include#includeusingnamespacestd;classPoint/类的声明同例6-1

44、6;classArrayOfPoints/动态数组类public:ArrayOfPoints(intsize):size(size)points=newPointsize;ArrayOfPoints()coutDeleting.=0&indexsize);returnpointsindex;private:Point*points;/指向动态数组首地址intsize;/数组大小;6.3 动态内存分配例6-18 (续)95intmain()intcount;coutcount;ArrayOfPointspoints(count);/创建对象数组/通过访问数组元素的成员points.element

45、(0).move(5,0);/通过类访问数组元素的成员points.element(1).move(15,20);return0;6.3 动态内存分配96例6-18 (续)运行结果:Pleaseenterthenumberofpoints:2DefaultConstructorcalled.DefaultConstructorcalled.Deleting.Destructorcalled.Destructorcalled.6.3 动态内存分配976.3 动态内存分配动态创建多维数组 new 类型名T第1维长度第2维长度;如果内存申请成功,new运算返回一个指向新分配内存首地址的指针,是一个T

46、类型的数组,数组元素的个数为除最左边一维外各维下标表达式的乘积。例如:char (*fp)3;fp = new char23;986.3 动态内存分配char(*fp)3;fpfp+1fp00fp01fp02fp10fp11fp12例6-19 动态创建多维数组99#includeusingnamespacestd;intmain()float(*cp)98=newfloat898;for(inti=0;i8;i+)for(intj=0;j9;j+)for(intk=0;k8;k+)/以指针形式数组元素*(*(*(cp+i)+j)+k)=static_cast(i*100+j*10+k);6.3

47、 动态内存分配例6-19 (续)100for(inti=0;i8;i+)for(intj=0;j9;j+)for(intk=0;k8;k+)/将指针cp作为数组名使用,通过数组名和下标访问数组元素coutcpijk;coutendl;coutendl;deletecp;return0;6.3 动态内存分配101用vector创建数组对象为什么需要vector?将动态数组封装,自动创建和删除数组下标越界检查例6-18中封装的ArrayOfPoints也提供了类似功能,但只适用于一种类型的数组vector动态数组对象的定义vector 数组对象名(数组长度);例:vector arr(5)建立大小

48、为5的int数组6.4 用vector创建数组对象1026.4 用vector创建数组对象vector数组对象的使用对数组元素的引用与普通数组具有相同形式:数组对象名 下标表达式 但vector数组对象名不表示数组首地址获得数组长度用size函数数组对象名.size()例6-20 vector应用举例103#include#includeusingnamespacestd;/计算数组arr中元素的平均值doubleaverage(constvector&arr)doublesum=0;for(unsignedi=0;iarr.size();i+)sum+=arri;returnsum/arr.

49、size();6.4 用vector创建数组对象例6-20 (续)104intmain()unsignedn;coutn;vectorarr(n);/创建数组对象coutPleaseinputnrealnumbers:endl;for(unsignedi=0;iarri;coutAverage=average(arr)endl;return0;6.4 用vector创建数组对象1056.5 深拷贝与浅拷贝深拷贝与浅拷贝浅拷贝实现对象间数据元素的一一对应复制。深拷贝当被复制的对象数据成员是指针类型时,不是复制该指针成员本身,而是将指针所指对象进行复制。例6-21 对象的浅拷贝106#includ

50、e#includeusingnamespacestd;classPoint/类的声明同例6-16/;classArrayOfPoints/类的声明同例6-18/;6.5 深拷贝与浅拷贝例6-21 (续)107intmain()intcount;coutcount;ArrayOfPointspointsArray1(count);/创建对象数组pointsArray1.element(0).move(5,10);pointsArray1.element(1).move(15,20);ArrayOfPointspointsArray2=pointsArray1;/创建副本coutCopyofpoi

51、ntsArray1:endl;coutPoint_0ofarray2:pointsArray2.element(0).getX(),pointsArray2.element(0).getY()endl;coutPoint_1ofarray2:pointsArray2.element(1).getX(),pointsArray2.element(1).getY()endl;6.5 深拷贝与浅拷贝例6-21 (续)108pointsArray1.element(0).move(25,30);pointsArray1.element(1).move(35,40);coutAfterthemoving

52、ofpointsArray1:endl;coutPoint_0ofarray2:pointsArray2.element(0).getX(),pointsArray2.element(0).getY()endl;coutPoint_1ofarray2:pointsArray2.element(1).getX(),pointsArray2.element(1).getY()endl;return0;6.5 深拷贝与浅拷贝例6-21 (续)109运行结果如下:Pleaseenterthenumberofpoints:2DefaultConstructorcalled.DefaultConstruc

53、torcalled.CopyofpointsArray1:Point_0ofarray2:5,10Point_1ofarray2:15,20AfterthemovingofpointsArray1:Point_0ofarray2:25,30Point_1ofarray2:35,40Deleting.Destructorcalled.Destructorcalled.Deleting.接下来程序出现异常,也就是运行错误。6.5 深拷贝与浅拷贝1106.5 深拷贝与浅拷贝拷贝前拷贝后pointsArray1的数组元素占用的内存pointssizepointsArray1pointssizepoin

54、tsArray1pointsArray1的数组元素占用的内存pointssizepointsArray2例6-22 对象的深拷贝111#include#includeusingnamespacestd;classPoint/类的声明同例6-16;classArrayOfPointspublic:ArrayOfPoints(constArrayOfPoints&pointsArray);/其他成员同例6-18;ArrayOfPoints:ArrayOfPoints(constArrayOfPoints&v)size=v.size;points=newPointsize;for(inti=0;is

55、ize;i+)pointsi=v.pointsi;intmain()/同例6-206.5 深拷贝与浅拷贝例6-22 (续)112程序的运行结果如下:Pleaseenterthenumberofpoints:2DefaultConstructorcalled.DefaultConstructorcalled.DefaultConstructorcalled.DefaultConstructorcalled.CopyofpointsArray1:Point_0ofarray2:5,10Point_1ofarray2:15,20AfterthemovingofpointsArray1:Point_0

56、ofarray2:5,10Point_1ofarray2:15,20Deleting.Destructorcalled.Destructorcalled.Deleting.Destructorcalled.Destructorcalled.6.5 深拷贝与浅拷贝1136.5 深拷贝与浅拷贝拷贝前pointsArray1的数组元素占用的内存pointssizepointsArray1拷贝后pointssizepointsArray1pointsArray1的数组元素占用的内存pointssizepointsArray21146.6 字符串6.6.1 用字符数组存储和处理字符串字符串常量(例:pr

57、ogram)各字符连续、顺序存放,每个字符占一个字节,以0结尾,相当于一个隐含创建的字符常量数组“program”出现在表达式中,表示这一char数组的首地址首地址可以赋给char常量指针:const char *STRING1 = program;字符串变量可以显式创建字符数组来表示字符串变量,例如,以下三条语句具有等价的作用:char str8 = p, r, o, g, r, a, m, 0 ;char str8 = program;char str = program;p pr ro og gr ra am m001156.6 字符串 6.6.1 用字符数组存储和处理字符串用字符数组表

58、示字符串的缺点用字符数组表示字符串的缺点执行连接、拷贝、比较等操作,都需要显式调用库函数,很麻烦当字符串长度很不确定时,需要用new动态创建字符数组,最后要用delete释放,很繁琐字符串实际长度大于为它分配的空间时,会产生数组下标越界的错误解决方法使用字符串类string表示字符串string实际上是对字符数组操作的封装1166.6 字符串6.6.2 string类常用构造函数string(); /缺省构造函数,建立一个长度为0的串string(const char *s); /用指针s所指向的字符串常量初始化string类的对象string(const string& rhs); /拷贝构

59、造函数例:string s1; /建立一个空字符串string s2 = “abc”; /用常量建立一个初值为”abc”的字符串string s3 = s2; /执行拷贝构造函数,用s2的值作为s3的初值1176.6 字符串6.6.2 string类(续)常用操作符s + t将串s和t连接成一个新串s = t用t更新ss = t判断s与t是否相等s != t判断s与t是否不等s t判断s是否小于t(按字典顺序比较)s t判断s是否大于t (按字典顺序比较)s = t判断s是否大于或等于t (按字典顺序比较)si访问串中下标为i的字符例:string s1 = “abc”, s2 = “def”

60、;string s3 = s1 + s2;/结果是”abcdef”bool s4 = (s1 s2);/结果是truechar s5 = s21;/结果是e例6-23 string类应用举例118#include#includeusingnamespacestd;/根据value的值输出true或false,title为提示文字inlinevoidtest(constchar*title,boolvalue)couttitlereturns(value?true:false)endl;6.6 字符串 6.6.2 string类例6-23 (续)119intmain()strings1=DEF;

61、couts1iss1endl;strings2;couts2;coutlengthofs2:s2.length()endl;/比较运算符的测试test(s1=ABC,s1=ABC);test(DEF=s1,DEF=s1);/连接运算符的测试s2+=s1;couts2=s2+s1:s2endl;coutlengthofs2:s2.length()操作符输入字符串,会以空格作为分隔符,空格后的内容会在下一回输入时被读取用string头文件中的getline可以输入整行字符串,例如:getline(cin, s2);以其它字符作为分隔符输入字符串输入字符串时,可以使用其它分隔符作为字符串结束的标志(

62、例如逗号、分号)把分隔符作为getline的第3个参数即可,例如:getline(cin, s2, ,);6.6 字符串 6.6.2 string类例6-24 用getline输入字符串121include#includeusingnamespacestd;intmain()for(inti=0;i2;i+)stringcity,state;getline(cin,city,);getline(cin,state);coutCity:city“State:stateendl;return0;6.6 字符串 6.6.2 string类运行结果:Beijing,ChinaCity: Beijing

63、 State: ChinaSan Francisco,the United StatesCity: San Francisco State: the United States6.7 综合实例个人银行账户管理程序改用字符串来表示银行账号多个账户组织在一个数组中,这样可以把需要对各个账户做的事情放在循环中,避免了代码的冗余。将日期用一个类来表示,内含年、月、日三个数据成员,同时设计计算相差天数整个程序分为5个文件:date.h是日期类头文件,date.cpp是日期类实现文件,account.h是储蓄账户类定义头文件,account.cpp是储蓄账户类实现文件,6_25.cpp是主函数文件。122

64、/date.h#ifndef_DATE_H_#define_DATE_H_classDate/日期类private:intyear;/年intmonth;/月intday;/日inttotalDays;/该日期是从公元元年1月1日开始的第几天public:Date(intyear,intmonth,intday);/用年、月、日构造日期intgetYear()constreturnyear;intgetMonth()constreturnmonth;intgetDay()constreturnday;intgetMaxDay()const;/获得当月有多少天1236.7 综合实例个人银行账户管

65、理程序例6-25boolisLeapYear()const/判断当年是否为闰年returnyear%4=0&year%100!=0|year%400=0;voidshow()const;/输出当前日期/计算两个日期之间差多少天intdistance(constDate&date)constreturntotalDays-date.totalDays;#endif/_DATE_H_1246.7 综合实例个人银行账户管理程序例6-25/date.cpp#includedate.h#include#includeusingnamespacestd;namespace /namespace使下面的定义

66、只在当前文件中有效/存储平年中的某个月1日之前有多少天,为便于getMaxDay函数的实现,该数组多出一项constintDAYS_BEFORE_MONTH=0,31,59,90,120,151,181,212,243,273,304,334,365;Date:Date(intyear,intmonth,intday):year(year),month(month),day(day)if(daygetMaxDay()coutInvaliddate:;show();cout2)totalDays+;intDate:getMaxDay()constif(isLeapYear()&month=2)r

67、eturn29;elsereturnDAYS_BEFORE_MONTHmonth-DAYS_BEFORE_MONTHmonth-1;voidDate:show()constcoutgetYear()-getMonth()-getDay();1266.7 综合实例个人银行账户管理程序例6-25(续)/account.h#ifndef_ACCOUNT_H_#define_ACCOUNT_H_#includedate.h#includeclassSavingsAccount/储蓄账户类private:std:stringid;/帐号doublebalance;/余额doublerate;/存款的年利

68、率DatelastDate;/上次变更余额的时期doubleaccumulation;/余额按日累加之和staticdoubletotal;/所有账户的总金额/记录一笔帐,date为日期,amount为金额,desc为说明voidrecord(constDate&date,doubleamount,conststd:string&desc);/报告错误信息voiderror(conststd:string&msg)const;1276.7 综合实例个人银行账户管理程序例6-25(续)/获得到指定日期为止的存款金额按日累积值doubleaccumulate(constDate&date)cons

69、treturnaccumulation+balance*date.distance(lastDate);public:SavingsAccount(constDate&date,conststd:string&id,doublerate);/构造函数conststd:string&getId()constreturnid;doublegetBalance()constreturnbalance;doublegetRate()constreturnrate;staticdoublegetTotal()returntotal;voiddeposit(constDate&date,doubleamo

70、unt,conststd:string&desc);/存入现金voidwithdraw(constDate&date,doubleamount,conststd:string&desc);/取出现金/结算利息,每年1月1日调用一次该函数voidsettle(constDate&date);voidshow()const;/显示账户信息;#endif/_ACCOUNT_H_1286.7 综合实例个人银行账户管理程序例6-25(续)/account.cpp#includeaccount.h#include#includeusingnamespacestd;doubleSavingsAccount:

71、total=0;/SavingsAccount类相关成员函数的实现SavingsAccount:SavingsAccount(constDate&date,conststring&id,doublerate):id(id),balance(0),rate(rate),lastDate(date),accumulation(0)date.show();coutt#idcreatedendl;voidSavingsAccount:record(constDate&date,doubleamount,conststring&desc)accumulation=accumulate(date);las

72、tDate=date;amount=floor(amount*100+0.5)/100;/保留小数点后两位balance+=amount;total+=amount;date.show();coutt#idtamounttbalancetdescendl;1296.7 综合实例个人银行账户管理程序例6-25(续)voidSavingsAccount:error(conststring&msg)constcoutError(#id):msggetBalance()error(notenoughmoney);elserecord(date,-amount,desc);voidSavingsAcco

73、unt:settle(constDate&date)doubleinterest=accumulate(date)*rate/计算年息/date.distance(Date(date.getYear()-1,1,1);if(interest!=0)record(date,interest,interest);accumulation=0;voidSavingsAccount:show()constcoutidtBalance:balance;1306.7 综合实例个人银行账户管理程序例6-25(续)/6_25.cpp#includeaccount.h#includeusingnamespace

74、std;intmain()Datedate(2008,11,1); /起始日期/建立几个账户SavingsAccountaccounts=SavingsAccount(date,03755217,0.015),SavingsAccount(date,02342342,0.015);/账户总数constintn=sizeof(accounts)/sizeof(SavingsAccount);/11月份的几笔账目accounts0.deposit(Date(2008,11,5),5000,salary);accounts1.deposit(Date(2008,11,25),10000,sellst

75、ock0323);1316.7 综合实例个人银行账户管理程序例6-25(续)/12月份的几笔账目accounts0.deposit(Date(2008,12,5),5500,salary);accounts1.withdraw(Date(2008,12,20),4000,buyalaptop);/结算所有账户并输出各个账户信息coutendl;for(inti=0;in;i+)accountsi.settle(Date(2009,1,1);accountsi.show();coutendl;coutTotal:SavingsAccount:getTotal()endl;return0;1326

76、.7 综合实例个人银行账户管理程序例6-25(续)例6-25(续)运行结果如下:2008-11-1#S3755217created2008-11-1#02342342created2008-11-5#S375521750005000salary2008-11-25#023423421000010000sellstock03232008-12-5#S3755217550010500salary2008-12-20#02342342-40006000buyalaptop2009-1-1#S375521717.7710517.8interestS3755217Balance:10517.82009-

77、1-1#0234234213.26013.2interest02342342Balance:6013.2Total:165311336.7 综合实例个人银行账户管理程序指针与引用的对应关系/使用指针常量voidswap(int*constpa,int*constpb)inttemp=*pa;*pa=*pb;*pb=temp;intmain()inta,b;swap(&a,&b);return0;/使用引用voidswap(int&ra,int&rb)inttemp=ra;ra=rb;rb=temp;intmain()inta,b;swap(a,b);return0;1346.8 深度探索 6.

78、8.1 指针与引用指针与引用的联系引用在底层通过指针来实现一个引用变量,通过存储被引用对象的地址,来标识它所引用的对象引用是对指针的包装,比指针更高级指针是C语言就有的底层概念,使用起来很灵活,但用不好容易出错引用隐藏了指针的“地址”概念,不能直接对地址操作,比指针更安全1356.8 深度探索 6.8.1 指针与引用引用与指针的选择什么时候用引用?如无需直接对地址进行操作,指针一般都可用引用代替用更多的引用代替指针,更简洁、安全什么时候用指针?引用的功能没有指针强大,有时不得不用指针:引用一经初始化,无法更改被引用对象,如有这种需求,必须用指针;没有空引用,但有空指针,如果空指针有存在的必要,

79、必须用指针;函数指针;用new动态创建的对象或数组,用指针存储其地址最自然;函数调用时,以数组形式传递大量数据时,需要用指针作为参数。1366.8 深度探索 6.8.1 指针与引用指针的地址安全性问题地址安全性问题通过指针,访问了不该访问的地址,就会出问题典型问题:数组下标越界问题的严重性:有时会在不知不觉中产生错误,错误源很难定位,因此程序调试起来很麻烦解决方案指针只有赋了初值才能使用(这一点普通变量也应遵循)指针的算术运算,一定要限制在通过指向数组中某个元素的指针,得到指向同一个数组中另一个元素的指针尽量使用封装的数组(如vector),而不直接对指针进行操作1376.8 深度探索 6.8

80、.2 指针的安全性隐患及其应对方案指针的类型安全性问题基本类型数据的转换是基于内容的:例:int i = 2;float x = static_cast(i);目标类型不同的指针间的转换,会使一种类型数据的二进制序列被当作另一种类型的数据:reinterpret_cast:可以将一种类型的指针转换为另一种类型int i = 2;float *p = reinterpret_cast(&i);结论从一种具体类型指针转换为另一种具体类型指针的reinterpret_cast很不安全,一般情况下不要用1386.8 深度探索 6.8.2 指针的安全性隐患及其应对方案指针的类型安全性问题(续)void指

81、针与具体类型指针的转换:具体类型指针可以隐含地转换为void指针:int i = 2;void *vp = &i;void指针转换为具体类型指针需要用static_cast,例如:int *p = static_cast(vp);但这样就出问题了:float *p2 = static_cast(vp);结论void指针也容易带来不安全,尽量不用,在不得不用的时候,一定要在将void指针转换为具体类型指针时,确保指针被转换为它最初的类型。1396.8 深度探索 6.8.2 指针的安全性隐患及其应对方案堆对象的管理堆对象必须用delete删除避免“内存泄漏”原则很简单,但在复杂的程序中,一个堆对象

82、常常要被多个不同的类、模块访问,该在哪里删除,常常引起混乱如何有条理地管理堆对象明确每个堆对象的归属最理想的情况:在一个类的成员函数中创建的堆对象,也在这个类的成员函数中删除把对象的建立和删除变成了一个类的局部问题如需在不同类之间转移堆对象的归属,一定要在注释中注明,作为类的对外接口约定例如在一个函数内创建堆对象并将其返回,则该对象应当由调用该函数的类负责删除1406.8 深度探索 6.8.2 指针的安全性隐患及其应对方案const_cast的应用const_cast的用法用于将常指针、常引用转换为不带“const”的相关类型例:void foo(const int *cp) int *p =

83、 const_cast(cp);(*p)+;这可以通过常指针修改指针对象的值,但这是不安全的用法,因为破坏了接口中const的约定const_cast不是安全的转换,但有时可以安全地使用1416.8 深度探索 6.8.3 const_cast的应用const_cast的安全使用对例6-18的改进例6-18通过下列函数访问数组元素Point &element(int index);问题:由于不是常成员函数,无法使用常对象访问数组元素解决方法:重载element函数:const Point &element(int index) const assert(index = 0 & index siz

84、e);return pointsindex;新问题代码的重复:这个element函数与原先的函数内容完全相同,两份重复的代码,不易维护1426.8 深度探索 6.8.3 const_cast的应用const_cast的安全使用(续)新问题的解决修改原先的element函数:Point &element(int index) return const_cast( static_cast(this) -element(index);执行过程:调用常成员函数element(),再将其返回结果中的const用const_cast去除将this用static_cast转换为常指针,是安全的转换该函数本身不是常成员函数,确保将最终的结果以普通引用形式返回是安全的思考:如果保留该函数,而修改常成员函数element,使常成员函数element调用该函数,是否合适?1436.8 深度探索 6.8.3 const_cast的应用小结1446.9 小结主要内容数组、指针、动态存储分配、指针与数组、指针与函数、字符串达到的目标理解数组、指针的概念,掌握定义和使用方法,掌握动态存储分配技术,会使用string类。

展开阅读全文
相关资源
正为您匹配相似的精品文档
相关搜索

最新文档


当前位置:首页 > 高等教育 > 研究生课件

电脑版 |金锄头文库版权所有
经营许可证:蜀ICP备13022795号 | 川公网安备 51140202000112号