《吉大课件C程序结构.ppt》由会员分享,可在线阅读,更多相关《吉大课件C程序结构.ppt(69页珍藏版)》请在三一办公上搜索。
1、-1-,C+Programming Language,Dr.Zheng XiaojuanAssociate ProfessorSoftware College of Northeast Normal UniversityOctober.2008,-2-,第五章 程序结构,-3-,本 章 内 容,作用域与可见性生存期 局部对象和全局对象 静态成员 友元 常类型编译预处理,-4-,类本身可被定义在三种作用域内:1.全局作用域。全局类,绝大多数的C+类是定义在该作用域中,我们在前面定义的所有类都是在全局作用域中。2.在另一个类的作用域中。嵌套类,即一个类包含在另一个类中。3.在一个块的局部作用域中。
2、局部类,该类完全被块包含。,1 作用域与可见性,-5-,2 生存期,2.1 静态生存期 1.定义:与程序的运行期相同。静态生存期的变量程序开始运行就存在,直到程序运行结束生存期结束。具有文件作用域的变量具有静态生存期。2.语法声明:static。例如,static int k;3.分配空间:在固定的数据区域内分配空间。4.初始化:如果具有静态生存期的变量未初始化,则自动初始化为0。5.静态变量种类:全局变量、静态局部变量。,-6-,运行时的存储空间结构,目标代码空间,寄存器空间,静态区空间,堆区空间栈区空间,最大地址,最小地址,-7-,2.2 局部生存期 1.定义:在块作用域中声明的变量具有局
3、部生存期。此生存期诞生于声明点,而终止于其作用域的结束处。2.局部变量种类:具有局部生存期的变量,具有块作用域。块作用域内静态变量,具有静态生存期。例如:void main()static int k;/.,-8-,2.3 动态生存期 定义:由程序中特定的函数(malloc()和free()调用或由操作符(new和delete)创建和释放。具有动态生存期的变量在内存的堆区分配空间。,-9-,#includeclass Clock private:int Hour,Minute,Second;public:Clock()Hour=0;Minute=0;Second=0;,3 局部对象和全局对象,
4、-10-,void SetTime(int NewH,int NewM,int NewS);void ShowTime();Clock();void Clock:SetTime(int NewH,int NewM,int NewS)Hour=NewH;Minute=NewM;Second=NewS;void Clock:ShowTime()coutHour:Minute:Secondendl;,-11-,Clock globClock;void main()coutFirst time output:endl;globClock.ShowTime();globClock.SetTime(10,
5、30,45);Clock myclock(globClock);coutSecond time output:endl;myclock.ShowTime();,程序运行结果为First time output:0:0:0Second time output:10:30:45,-12-,#includeusing namespace std;int i;/文件作用域int main()i=5;int i;/块作用域 i=7;couti=iendl;/输出7 couti=i;/输出5 return 0;,作用域与可见性例,-13-,#includeusing namespace std;int i
6、=5;/文件作用域int main()couti=iendl;return 0;i具有静态生存期,对象的生存期例,-14-,#includeusing namespace std;void fun();int main()fun();fun();void fun()static int a=1;int i=5;a+;i+;couti=i,a=aendl;,运行结果:i=6,a=2i=6,a=3i是动态生存期a是静态生存期,对象的生存期例,-15-,#includeusing namespace std;int i=1;/i 为全局变量,具有静态生存期。int main()static int
7、a;/静态局部变量,有全局寿命,局部可见。int b=-10;/b,c为局部变量,具有动态生存期。int c=0;void other(void);cout-MAIN-n;cout i:i a:a b:b c:cendl;c=c+8;other();cout-MAIN-n;cout i:i a:a b:b c:cendl;i=i+10;other();,对象的生存期例,-16-,void other(void)static int a=2;static int b;/a,b为静态局部变量,具有全局寿命,局部可见。/只第一次进入函数时被初始化。int c=10;/C为局部变量,具有动态生存期,/
8、每次进入函数时都初始化。a=a+2;i=i+32;c=c+5;cout-OTHER-n;cout i:i a:a b:b c:cendl;b=a;,17,-17-,void Render(Image/非法,没有定义ColorTable类!,-18-,静态数据成员用关键字static声明该类的所有对象维护该成员的同一个拷贝必须在类外定义和初始化,用(:)来指明所属的类。静态成员函数类外代码可以使用类名和作用域操作符来调用静态成员函数。静态成员函数只能引用属于该类的静态数据成员或静态成员函数。,4 静态成员,-19-,4.1 静态数据成员 类的普通数据成员:类的静态数据成员:,在类的每一个对象中都
9、拥有一个拷贝,每个对象的同名数据成员可以分别存储不同的数值,这也是每个对象拥有自身特征的保证。,是类的数据成员的一种特例。每个类只有一个静态数据成员拷贝,它由该类的所有对象共同维护和使用,从而实现了同一个类的不同对象之间的数据共享。静态数据成员具有静态生存期。,-20-,2.应用要点:(1)声明:加static关键字说明。例如:static int n(2)初始化:在定义时初始化,且必须在类和所有的成员函数之外,与全局变量初始化的方法一样。不能在构造函数中初始化。因为静态数据成员在构造函数被调用之前就已经存在了。形式::=例如:int point:n=0;(3)引用:静态数据成员属于类,而不属
10、于任何一个对象,所以,在类外通过类名对它进行引用。一般形式如下::;,-21-,(4)静态数据成员访问控制限制:private/protected:只能在类内通过公有的静态成员函数访问,在类外无法引用。public:可以采用“类名:成员名”或“对象名.成员名”访问。,-22-,【例4】含有静态数据成员。#includeclass Test private:int k;public:static int n;Test(int kk)k=kk;n+;void Display()coutn=n k=kendl;,n为公有静态数据成员,用来给Test类的对象计数,每声明一个新对象,n的值就相应加l。,
11、-23-,int Test:n=0;/静态数据成员初始化void main()Test t1(10);t1.Display();Test t2(20);t2.Display();Test:n+;(t1.n+或t2.n+)t2.Display();,三次调用均访问的是t1和t2共同维护的该静态成员的拷贝,这样实现了在t1和t2两个对象间数据共享。,程序运行结果为:n=1 k=10n=2 k=20n=3 k=20,引用方法,-24-,练习:如果我们保持程序的其它部分不变,只将主函数中改为void main()Test t1(10),t2(20);t1.Display();t2.Display();
12、Test:n+;t2.Display();程序运行结果?,-25-,4.2 静态函数成员 1.定义:使用static关键字声明的函数成员。同静态数据成员一样,静态函数成员也属于整个类,由同一个类的所有对象共同维护,为这些对象所共享。2.静态成员函数如何访问成员数据:静态的成员函数没有this指针(1)访问静态成员数据,可以直接使用其变量名来访问;(2)访问非静态成员数据,需要使用“对象名.成员名”;3.公有的、静态的成员函数在类外的调用方式:类名:成员函数名(实参表)4.允许用对象调用静态的成员函数。,-26-,【例5】使用静态函数成员。#include class Mpublic:M(int
13、 a)A=a;B+=a;static void f1(M m);private:int A;static int B;,void M:f1(M m)cout“A=“m.Aendl;cout“B=“Bendl;int M:B=0;void main()M P(5),Q(10);M:f1(P);M:f1(Q);,访问静态成员数据,可以直接使用其变量名,访问非静态成员数据,需要使用“对象名.成员名”;,结果:A=5 B=15 A=10 B=15,-27-,#includeclass A private:int x;public:static void f(A a);void A:f(A a)cout
14、x;/对x的引用是错误的 couta.x;/正确,-28-,【课堂练习】阅读程序,写出程序运行结果#includeclass pointprivate:int x,y;static int countP;public:point(int xx=0,int yy=0)x=xx;y=yy;countP+;point(point,int point:countP=0;void main()point:get_c();/利用类名引用静态函数成员point a(4,5);coutpoint a,a.get_x(),a.get_y();a.get_c();/利用对象名引用静态函数成员point b(a);
15、coutpoint b,b.get_x(),b.get_y();point:get_c();/利用类名引用静态函数成员,-29-,程序运行结果为:Object id=0 point a,4,5 Object id=1 point b,4,5 Object id=2,-30-,5 友元,1.含义:类的体内以friend加以限定的非成员函数,从而可以在此函数体内利用对象访问类中private成员。2.为什么需要友元:(1)在封装和快速性方面合理选择-类的主要特点是使实现数据隐藏,既不允许非成员函数对它访问,但在某些场合下,非成员函数体中需要通过对象名访问private成员,这可以通过友元函数来实现
16、。(2)有些函数需要放在外面或者类设计完后补充的,而不能成为成员函数,但是又需要访问类中的私有成员。3.作用:实现直接访问数据。,-31-,5.1 友元函数 1.定义:如果友元是普通函数或类的成员函数,则称为友元函数。友元函数是在类声明中由关键字friend修饰的非成员函数。2.语法:(1)普通函数声明为友元函数的形式:friend(参数表)(2)成员函数声明为友元函数的形式:friend:(参数表),-32-,3.应用要点:(1)友元函数的声明可以在类声明中的任何位置,既可在public区,也可在protected区,意义完全一样。(2)友元函数的定义一般放在类的外部,最好与类的其它成员函数
17、定义放在一起。如果是普通函数作为友元,也可以放在类中。(3)友元函数不是本类的成员函数,但是它可以通过对象名访问类的所有成员,包括私有和保护成员。(4)要慎用友元。,-33-,【例】普通函数作友元函数。#include#includeclass pointprivate:double x,y;public:point(double xx=0,double yy=0)x=xx;y=yy;double get_x()return x;double get_y()return y;friend double distance(point,-34-,double distance(point,-35-
18、,【例】成员函数作友元函数。#include#includeclass point;/前向声明point类class A public:double distance(point,-36-,public:point(double xx=0,double yy=0)x=xx;y=yy;double get_x()return x;double get_y()return y;friend double A:distance(point,-37-,void main()point myp1(1,1),myp2(4,5);A obj;/声明一个A类的对象coutThe distance is:obj
19、.distance(myp1,myp2)endl;,在主函数中一定要声明一个A类的对象。只有这样,才能通过对象名调用友元函数。,-38-,4.普通函数作友元函数与成员函数对比:(1)定义:原型定义-采用friend加以限定;函数体定义-不必采用“类名:”加以限定。(2)调用方面:友元函数可直接调用,不必采用对象方式调用。5.普通函数作友元函数与一般函数对比:(1)定义:需要在类中加以原型说明(注册为友好成员)(2)编程应用:友元函数体内可以访问类中的私有成员。成元函数 友元函数 一般函数“准成员函数”安全性-方便性 折中,-39-,5.2 友元类 1.如果友元是一个类,则称为友元类。2.语法声
20、明形式:friend class 3.应用要点:(1)友元类的声明同样可以在类声明中的任何位置。(2)友元类的所有成员函数都成为友元函数。例如,若A类为B类的友元类,则A类的所有成员函数都是B类的友元函数,都可以访问B类的私有和保护成员。,-40-,用友元类的方法重做例8。#include#includeclass point;/前向声明point类class Apublic:double distance(point class point,-41-,private:double x,y;public:point(double xx=0,double yy=0)x=xx;y=yy;doubl
21、e get_x()return x;double get_y()return y;friend class A;/A类为友元类;double A:distance(point,-42-,void main()point myp1(1,1),myp2(4,5);A obj;/必须声明一个A类的对象coutThe distance is:“obj.distance(myp1,myp2)endl;,友元类的成员函数可以通过对象名直接访问到隐藏的数据,达到高效协调工作的目的。,-43-,4.两点注意:友元关系是不能传递的。B类是A类的友元,C类是B类的友元,C类和A类之间如果没有声明,就没有任何友元关
22、系,不能进行数据共享。友无关系是单向的。如果声明B类是A类的友元,B类的成员函数就可以访问A类的私有和保护数据,但A类的成员函数却不能访问B类的私有和保护数据。,-44-,1.功能:类型修饰符,左结合。用来冻结一个变量的值,使其值在程序中不能被进一步改变(置成只读属性,即变为常量)。2.定义:const 类型定义符 变量名=初值;(当T是一个简单数据类型时 T const=const T)例如:const int sumValue=10;(或int const sumValue=10;)3.应用要点:(1)替代C中的预处理命令#define,但比它更加语义精确,因为#define无法反映常量的
23、数据类型。如:#define sumValue=10/此时的sumValue是char型还是int型,6 常类型(const),-45-,(2)对变量经const修饰后,必须给变量赋初值(但函数的形参不需赋初值),一经const修饰后便不能修改此变量之值 const int sumValue=10;sumValue=0;/错误void Display(const int*a,int n)couta0;/=*(a+0),显示数组中的第一个元素之值a0=1;/错误,不能修改其值(不能通过指针来改变目标单元之值),-46-,()区分修饰指针的两种不同用法:a)指向常对象的指针变量:const int
24、*p=p0=2.,const int*p int*const p=,-47-,()C+视const修饰的符号名为常量特性,因而它可以用于任何可能出现数字常量的场合(如数组的大小的说明等).const int ArraySize=10;char arrayArraySize;()由于C+视const修饰的符号名为常量特性,因而并不对它实际分配内存空间const int sumValue=10;/不实际分配内存空间 int sumValue=10;/实际分配内存空间(两个字节)()#define所定义的符号名为全局性常量,因而在整个程序中应保持唯一性;而const可以说明一个局部或全局性同名常量(
25、据此可以改变const符号名之值),-48-,#define Min 1const int Max=100void main(void)#define Min 2/错误,不能再定义同名常量 const int Max=200/可以再定义同名局部常量,-49-,7.1 常引用定义:使用const关键字声明的引用称为常引用。常引用所引用的对象不能被更新。语法:常引用的声明形式:const&3.适用场合:当函数的参数是指针或引用类型,而该函数又不更新这些参数所标识的对象时,这个参数应该使用const进行声明.C+中经常用指针和常引用做函数参数,这样既能保证数据安全,使数据不能被随意修改,在调用函数时
26、又不必建立实参的拷贝。,-50-,【例9】常引用。#includevoid main()int n=5;const int 这段程序编译时有两个错误:error C2530:m:references must be initializederror C2166:l-value specifies const object,-51-,将上述程序稍作修改,#includevoid main()int n=5;const int 这时,程序运行结果为m=5,-52-,【例10】常引用作形参。#includevoid display(const double error C2166:l-value s
27、pecifies const object,常引用做形参时,函数不能更新r所引用的对象,因此,对应的实参就不会被破坏。,-53-,7.2 常对象 定义:使用const关键字声明的对象称为常对象。语法:常对象的声明形式:const 或 const 声明常对象的同时,也要进行初始化,而且该对象以后不能再被更新。class A private:int x,y;,-54-,public:A(int i=0,int j=0)x=i;y=j;/.;A cobj1;A const cobj2(3,4);cobj1.print();cobj2.print();X/.error C2662:print:cann
28、ot convert this pointer from const class A to class A&,语法规定不能通过常对象调用普通的成员函数。,-55-,7.3 常成员函数 定义:使用const关键字声明的函数称为常成员函数。语法:常成员函数声明的形式如下:(参数表)const;3目的:const成员函数表示该成员函数只能读类数据成员,而不能修改类成员数据。即在不改变对象的成员函数的函数原型中加上const说明。可提高程序的可读性。还能提高程序的可靠性,已定义成const的成员函数,一旦企图修改数据成员的值,则编译器按错误处理。4应用要点:const是加在函数声明后面的类型修饰符,它
29、是函数类型的一个组成部分,因此,在实现部分也要带const关键字。,如果将const放在函数声明前意味着函数的返回值是常量,意义完全不同。,-56-,const关键字可以被用于对重载函数的区分。例如,可以在类中这样声明:void fun();void fun()const;常成员函数不能更新对象的数据成员,也不能调用该类中没有用const修饰的成员函数。如果将一个对象说明为常对象,则通过该对象只能调用它的常成员函数,而不能调用其它成员函数。在C+中,const对象只能调用const成员函数,-57-,【例11】常成员函数。#includeclass Aprivate:int x,y;publi
30、c:A(int i=0,int j=0)x=i;y=j;void fun()/成员函数 cout成员函数:x=x,y=yendl;void fun()const/常成员函数cout常成员函数:x=x,y=yendl;,-58-,void main()A obj1(1,2);obj1.fun();const A obj2(3,4);/A类常对象obj2.fun();,通过对象obi1调用的是一般的成员函数,而通过对象obj2调用的是用const修饰的常成员函数。,程序运行结果为成员函数:x=1,y=2常成员函数:x=3,y=4,-59-,7.4 常数据成员 1.定义:使用关键字const说明的数
31、据成员。包括常引用、常对象等,常数据成员不能被更新.2.初始化:用成员初始化列表的方式通过构造函数对该数据成员进行初始化。,-60-,【例12】常数据成员。#includeclass Aprivate:const int x;/常数据成员static const int y;/静态常数据成员public:const int/常引用,本程序有三个常数据成员,-61-,A(int i):x(i),r(x)/常数据成员通过初始化列表获得初值void fun()coutx=x,y=y,r=rendl;const int A:y=5;/静态常数据成员的初始化void main()A obj1(1),ob
32、j2(2);obj1.fun();obj2.fun();,程序运行结果为:x=1,y=5,r=1x=2,y=5,r=2,-62-,编 译 预 处 理,1.定义:预处理程序又称为预处理器,它包含在编译器中。预处理程序提供了一组编译预处理指令和预处理操作符。编译器在对源程序进行编译之前,首先要由预处理程序对程序文本进行预处理,然后编译器接受预处理程序的输出,并将源代码转化成用机器语言编写的目标文件。预处理指令实际上不是C+语言的一部分,它只是用来扩充C+程序设计的环境。,-63-,需预处理的源程序,预处理器,源程序,编译程序,目标汇编程序,汇编程序,可重定位的目标代码,连接/装配程序,绝对目标代码
33、,高级语言程序到可执行代码的转换过程,-64-,2.使用预处理指令需要注意以下几点:所有的预处理指令在程序中都是以“#”来引导。每一条预处理指令单独占用一行,不需要用分号结束。预处理指令可以根据需要出现在程序中的任何位置。3.预处理指令:,-65-,(1)#include指令 1)定义:#include指令也称文件包含指令,其作用是将另一个源文件嵌入到当前源文件中该点处。2)语法及功能:格式1:#include 功能:按标准方式搜索C+系统目录下的include子目录。格式2:#include“文件名”功能:首先在当前目录中搜索,若没有,再按标准方式搜索。,-66-,(2)#define和#u
34、ndef指令 1)定义:C,#define 符号常量 带参数的宏。#define PI 3.14/定义了一个符号常量PI#define MAX(a,b)(a)(b)?(a):(b)/定义一个带 参数的宏MAX(a,b)仍可这样定义 被const替代 inline内嵌函数#undef的作用是删除由#define定义的宏,使之不再起作用。,C+,-67-,(3)条件编译指令 1)定义:限定程序中的某些内容要在满足一定条件的情况下才参与编译。利用条件编译可以使同一个源程序在不同的编译条件下产生不同的目标代码。2)语法:形式1:#ifdef 标识符 程序段1#else 程序段2#endif,如果“标识
35、符”已经定义过,则编译程序段1,否则编译程序段2;如果没有程序段2,则#else可以省略。,-68-,形式2:#ifndef标识符 程序段1#else 程序段2#endif 例:#ifndef MYHEAD_H#define MYHEAD_H#endif本段代码能够保证符号MYHEAD_H只有一次定义。,如果“标识符”未被定义过,则编译程序段1,否则编译程序段2;如果没有程序段2,则#else可以省略。,-69-,由于文件包含指令可以嵌套使用,在设计程序时要避免多次重复包含统一个头文件,否则会引起变量及类的重复定义。,/main.cpp#include“file1.h”#include“file2.h”int main()/file1.h#include“head.h”/file2.h#include“head.h”/head.hclass point,/head.h#ifndef HEAD_H#define HEAD_HClass Point#enddif,