C++及Windows可视化程序设计第3章7204191565.ppt
《C++及Windows可视化程序设计第3章7204191565.ppt》由会员分享,可在线阅读,更多相关《C++及Windows可视化程序设计第3章7204191565.ppt(144页珍藏版)》请在淘文阁 - 分享文档赚钱的网站上搜索。
1、C+及及Windows可视可视化程序设计第化程序设计第3章章72041915653.7 结构3.8 联合实验习题虽然C+提供了基本数据类型,但它们的能力有限,还需要利用基本类型构造一些复杂的数据类型,这些以基本类型为基础构造出来的类型统称为构造类型。其实,构造类型的每一个分量都是一个对象,它可以是基本类型或者构造类型。这些分量可以与基本类型对象一样被赋值并在表达式中使用。合理地使用它们,不仅能准确、清晰地描述复杂的数据结构,而且还使得程序显得清晰、简洁。本节将探讨几个典型的构造类型,并简单说明它们的使用方法。随着应用的深入,还会构造出新的类型,这些构造类型相互之间又有一定联系,从而为程序设计提
2、供新的舞台。假设将一个整型对象x存放在以0012FF7C开始的内存单元中,如果要访问对象x,既可通过对象的名字x,也可通过该对象的首地址0012FF7C。可通过构造指针类型来实现这种操作。3.1 指针假设一个整型对象x的值为56,系统将为它在内存中分配一块连续的存储单元。如果这块存储区的首地址(即起始地址)为0012FF7C,则可以通过“&x”取得存放x的首地址(&称为取地址运算符,即&x=0012FF7C)。如果要访问对象x,既可以通过对象的名字x,也可以通过存放对象的首地址0012FF7C。如图3.1所示,假设使用一个运算符“*”来间接引用地址(&x)中存放对象的值,即“*&x”表示通过这
3、个地址访问对象x。下面的程序演示了这种方法,实现了所做假设。3.1.1 构造指针类型整型对象x存放的首地址0012FF7C地址运算:&x=0012FF7C赋值运算:x=56间接引用:*&x=5656图3.1 通过存放对象x的首地址间接引用对象x的值 #includeusing namespace std;void main() int x=56; coutx的值等于x ,存储x的首地址是&xendl; cout通过名字x使用x,通过首地址 &x使用*&xendl;程序输出结果如下:x的值等于56,存储x的首地址是0012FF7C通过名字x使用56,通过首地址0012FF7C使用56现在想使用一
4、个符号p代表&x,即p=&x。这时p代表x的地址,*p代表&x内的值x,即*p=x。运算符“*”使p间接引用了x的值,称为“间接引用”运算符或“递引用”运算符。这样,就可以将上面的第2条输出语句改写成如下形式: cout通过名字x使用x ,通过首地址p使用 *pendl;它实现了同样的输出结果,并验证了p和x之间的关系为: p=&x和 *p=x。既然p是一个标识符,也就是新数据类型的一个对象,系统也要给p分配一个存储空间。假设用内存单元0012FF78作为存放对象p的首地址,显然&p=0012FF78。不过,在这个地址0012FF78里不是存放p的值,而是存放变量x的首地址0012FF7C,即
5、p=&x。这样就可以根据p找到 x,也就能对x进行存取。对象p和x之间的关系如图3.2所示。图3.2 导出新的数据类型示意图现在的问题是,如何表示这种新的数据类型?首先,它是针对整型对象的,所以可以借助整型类型int来声明。“p”这种形式已经被基本类型的对象使用,如果使用“int p;”的形式,则将p作为整型对象,显然是行不通的。如果使用“*”来标记这种数据类型,就可将p与整型变量区分开来,也就解决了问题。即 int *p;/声明称为构造类型的指针类型对象 p=&x; /地址运算 *p=x; /值引用因为这种数据类型声明的对象代表指向另一个数据类型对象的存储首地址,所以得名为“指针”类型。这里
6、是借助“*”号,而且与“*”号的位置没有关系,下面3种写法都是正确的: int * p;/“*”在中间,与谁都不连 int *p;/“*”与p连写为*p int* p;/“*”与int连写为int*读者有时会对使用如下方法在声明指针的同时初始化指针的方式感到困惑,即: int *p=&x;实际上,选择“int* p;”,认为“int*”是一种指向整型的指针类型,用它声明指针对象p,p应该赋予x的地址,所以应是“p=&x”。声明指向整型的指针对象p并同时初始化,也就顺理成章为“int* p=&x”。显然,称p为指针对象(存放的是对象的首地址),而不是称*p为指针对象(*p代表指针指向的地址单元所
7、存放的值)。因为指针对象存放的是地址,所以必须有具体指向。最常见的错误是声明了指针,没有为指针赋值。没有赋值的指针里含有随机地址,破坏性很大。声明并同时初始化指针的方法避免了遗漏赋值。由此可知,p的值是地址,虽然这个地址就是对象x在内存中的存储首地址,但我们并不直接说p的值是x的地址,而说成p指向x的存储首地址,简称p指向x的地址。【例3.1】 演示使用指针及指针地址。#includeusing namespace std;void main()int x=56;int* p=&x;coutx的值等于x,x的首地址是 &x,p指向的地址是pendl;cout通过名字使用x,通过p内的地址 p使
8、用*pendl;coutp指向的地址为p,存放p的地址是 &pendl;程序输出如下:x的值等于56,x的首地址是0012FF7C,p指向的地址是0012FF7C通过名字使用56,通过p内的地址0012FF7C使用56p指向的地址为0012FF7C,存放p的地址是0012FF78假设已经知道对象地址(NULL 也算已知),现在将上面的构造语法总结如下: 存储类型数据类型*指针名; 指针名=对象地址;或者采取直接初始化的方法: 存储类型数据类型*指针名=对象地址;默认的存储类型为自动存储类型(auto),目前也仅以自动存储类型为例,以后将通过例子进一步介绍存储类型。现在假设它们具有如图3.3所示
9、的形式: 3.1.2 指针类型及指针运算图3.3 指针操作关系图由关联关系可知,*p和x同步变化,即改变任何一个的值,它们的值保持一致。如果改变p的内容,例如使用语句“p=&y;”,这使得*p=65,它与y 同步,不再与x有任何关系。【例3.2】演示了这种情况。从例子中可见,指针本身的地址不会变化,它反映了系统需要为指针p分配地址这一概念。正如使用x不要再考虑&x一样,以后也不再考虑&p。【例3.2】 演示指针及其运算概念的例子。#include using namespace std; void main() int x=56,y=65,*p;p=&x;/指针指向xcoutx,p,&p, *
10、pendl;p=&y;/指针改为指向ycouty, p,&p, *pendl ;*p=85;/通过指针改变对象内容 couty, p,&p, *pendl ;+p; /对指针进行增1运算 coutx,p,&p, *pendl ;-p; /对指针进行减1运算 coutx,p,&p, *pendl ;p=p-2;/对指针进行减2运算coutx,p,&p, *pendl ; coutx,p,&p, *(p+2)endl ; coutx,p,&p, *pendl ;*p=*(p+2); coutx,p,&p, *p”(由键盘上的-和两键组成)运算符访问对象的成员,即: 对象指针名-对象成员名3.1.4
11、 进一步讨论指针【例3.3】 演示对象指针及访问对象成员的例子。#include#includeusing namespace std;void main()string str(How are you?);string *p=&str;/初始化对象指针p /str使用“.”访问str的成员函数size()coutstr size=str.size()”访问str的成员函数size() cout*p size=size()endl;coutsubstr(4,8) size= size()-4endl;使用对象和对象指针的效果一样,这从下面程序的输出中可以得到证实: How are you? s
12、ize=12 How are you? size=12 are you? size=82. void指针一般情况下,指针的值只能赋给相同类型的指针。void类型不能声明变量对象,但可以声明void类型的指针,而且void指针可以指向任何类型的C+对象。下面给出一个使用void指针的例子。【例3.4】 演示void指针的例子。#includeusing namespace std;void main()int x=56,y=65,*p=&x;void *vp=&x;/void指针指向xcoutvp,p,x endl; /*vp不能间接引用对象x的值vp=&y;/void指针改为指向yp=(int
13、*)vp; /强制将void指针赋值给整型指针coutvp,p,*p endl; /*p可以间接引用对象y的值虽然void指针指向整型变量对象x,但不能使用*vp引用整型对象的值。要引用这个值,必须强制将void指针赋值给与值相对应的整型指针类型。程序输出如下: 0012FF7C,0012FF7C, 56 0012FF78, 0012FF78, 653. 指针的指针可以将定义指针的方法推广下去,定义指向指针的指针,不过并不建议使用多级指针。假如p1是指向整数对象的一级指针,语句“int *p2=&p1”定义了二级指针。可以通过下面的语句演示多级指针的定义及输出对象x的方法。 int x=88;
14、 /定义多级指针 int *p1=&x,*p2=&p1,*p3=&p2; cout*p1,*p2, *p3endl;/输出: 88,88,884. 自己给指针分配地址如果不使用对象地址初始化指针,可以自己给它分配地址。4. 自己给指针分配地址如果不使用对象地址初始化指针,可以自己给它分配地址。对于只存储一个基本类型数据的指针,如果为它申请可以存储size个该数据类型的对象,则申请的方式为: new 类型名size例如申请可以存储3个double类型的对象: double *p;/声明double型指针 p=new double3;/分配3个double型长度的地址不再使用时,简单地使用如下方式
15、释放存储空间: delete 指针名【例3.5】 演示使用new和delete的例子。#includeusing namespace std; void main()double *p;/声明double型指针p=new double3;/分配3个double型长度的地址for(int i=0;i*(p+i);/将输入数据存入指定地址for(i=0;i3;i+) cout*(p+i) ;/将地址里的内容输出delete p; 在同一行输入“12.1 12.2 12.3”,则输出: 12.1 12.2 12.3因为引用是指针通过“间接引用地址”实现存取对象的,所以语法比较难懂,也很容易出错。仍以
16、对象x=56为例,如果给对象x再起一个名字a,a与x的地址一样,则x和a同步变化。这就像一个作家,本来的名字叫“张三”,起个笔名叫“雨季”,则“雨季”即“张三”,“张三”即“雨季”。也就是说,给整数对象x起个“别名”,问题是如何起这个别名。一般的标识符已经被变量对象占用,“&”和“*”号也用掉了。既然这个别名与原来对象的地址有关,那就选定命名时使用地址符号“&”,再选用数据类型与之配合,声明方式为:3.2 引用 数据类型& 别名=对象名;图3.4是引用示意图。使用引用语句“int& a=x;”之前,对象x必须事先初始化。图图3.4 引用示意图引用示意图【例3.6】 演示引用的例子。#inclu
17、de using namespace std;void main()int x=56;int& a=x;/定义a是x的引用,a和x的地址一样int&r=a; /定义r是a的引用,r和a的地址一样 /即r和x的地址一样coutx=x,&x=&x,a= a,&a=&a ,r=r,&r=&rendl;r=25;/改变r,则a和x也同步变化coutx=x,&x=&x,a= a,&a=&a ,r=r,&r=&rendl;由输出结果可见,引用对象和被引用对象的地址一样,所以同步变化。程序输出如下: x=56,&x=0012FF7C,a=56,&a=0012FF7C,r=56,&r=0012FF7Cx=25
18、,&x=0012FF7C,a=25,&a=0012FF7C,r=25,&r=0012FF7C由此可见,所谓“引用”就是将一个新标识符和一块已经存在的存储区域相关联。因此,使用引用时没有分配新的存储区域,它本身并不是新的数据类型。引用在某些地方很像一个可以被逆向访问的常量指针。这里的逆向访问指的是可以通过引用来修改被引用的对象,同样可以通过被引用的对象来修改引用。可以对比一下常量指针和引用的使用规则: 创建一个引用时,该引用必须被初始化;常量指针亦是如此。 一旦一个引用被初始化指向一个对象,它不能被改变为对另外一个对象的引用;常量指针一旦指向了一个对象,也不能更改为指向另外的对象。 不能有空引用
19、。在程序中必须要确保引用是和一块正确的存储区域关联,但是可以有指向空的常量指针,例如这样的声明是正确的: int* const x=NULL; 但是这种指向空的常量指针在实际应用中并没有什么价值。引用通常用于函数的参数表中和函数的返回值。对引用实质的理解应抓住如下两点: 引用实际上就是变量的别名,使用引用就如同直接使用变量一样。引用与变量名在使用的形式上是完全一样的,但引用本身并不是对象,只是作为一种标识对象的手段。因此,引用有其与对象不同的特点,即不能声明引用的引用。语句“int& & r=x;”是对x引用的引用,是错误的。可以声明对指针的引用(如p2是指针,则int*& p1=p2;是可以
20、的),但不能声明指针对x的引用,即“int* & p=&x;”是错误的,但可以声明指向引用的指针“int* p=&a;”。 引用的作用与指针有相似之处,它会对内存地址上存在的变量进行修改,但它不占用新的地址,从而节省开销。但要注意,引用与指针是有区别的,除了使用形式上的不同以外,在本质上它们也有区别: 指针是低级的直接操作内存地址的机制,指针功能强大但使用不慎极易产生错误。在C+语言中,指针可由整型数强制类型转换得到,这就为不良程序的产生提供了可能,处理不当就可能对系统造成极大的破坏。另一方面,引用则是较高级地封装了指针的特性,它不直接操作内存地址,不可以由强制类型转换而得,因而具有较高的安全
21、性,也不容易产生由于使用指针而常常产生的那些不易察觉的错误。在处理对象时,使用引用不失为一种很好的选择。【例3.7】 演示指针引用的例子。#include using namespace std;void main()int x=56,*p1=&x;int& a=x; /a引用x,符号&的位置无关int*& p2=p1;/指针p2引用指针p1,*&的顺序不能错coutx=x,a=a,*p1= *p1,*p2=*p2endl;*p2=65; /通过引用的指针p2使它们同步变化coutx=x,a=a,*p1= *p1,*p2=*p2endl;int* p3=&a;/定义指针p3,使它指向x的别名a
22、 *p3=32; /通过指针p3使它们同步变化coutx=x,*p1=*p1,&x= &x,p3=p3endl;输出结果如下:x=56,a=56, *p1=56, *p2=56x=65,a=65, *p1=65, *p2=65x=32,*p1=32,&x=0012FF7C, p3=0012FF7C【例3.8】 演示string对象引用的例子。#include #include using namespace std;void main() string s1=We are here!;string&str=s1; /str引用s1string* sp1=&s1; /string指针sp1指向对
23、象s1string*& sp2=sp1;/指针sp2引用指针sp1coutstr=str,*sp1=*sp1, *sp2=*sp2endl;*sp2=Go home!;/通过引用指针使它们同步变化coutstr=str,*sp1= *sp1,s1=s1endl;输出结果如下:str=We are here!,*sp1=We are here!,*sp2=We are here!str=Go home!, *sp1=Go home!, s1=Go home!数组是另一种构造型数据类型。本节重点讨论一维数组,简要介绍多维数组。3.3 数组假如要表示5个连续整数对象15,则需要5个整数对象名称,例如
24、用X1X5表示。这批对象的特点是它们的基本数据类型一样。3.3.1 一维数组图3.5 数组构成示意图现现在构造一个新的数据类型,假设它的名称为在构造一个新的数据类型,假设它的名称为A,在符号,在符号“ ”内用序号表示为内用序号表示为A 0A 4,它们的对应关系如图它们的对应关系如图3.5所示:所示: 后一种表示有很大的进步。X1X5之间没有内在关系,而A0A4之间是通过“”内的序号04(也就是下标)构成了惟一的连续对应关系。如果把A0A4看做连续的房间,则可以改变房间里所放整数的值。暂且将A称做整数房间。由此可见,房间里必须存放同一种对象,这样构造出来的数据类型称为整型数组。假定使用语法定义如
25、下: int A5=1,2,3,4,5;它的含义是整型数组A的下标从0开始,A4的值为5,A5本身不是数组的元素。这个数字5代表数组A共有5个元素A0A4。由此可见,把若干个同类对象线性地组合起来,就构成一维数组。在使用一维数组之前首先必须声明它,这包括数组的类型、数组名和数组元素的个数。声明一维数组的一般方式如下: type array_namen;其中,type是数据类型,可以是已经介绍过的基本数据类型,也可以是即将介绍的构造类型。array_name为数组名(标识符),n为数组中所包含的数组元素的个数。数组与前面介绍的各种基本数据类型对象的不同之处是: 数组须由type、数组标志 及长度
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- C+ Windows 可视化 程序设计 7204191565
限制150内