第5章程序结构第5章程序结构5.1作用域与可见性5.2生存期5.3局部变量和全局变量5.4静态成员5.5友元5.6常类型5.7多文件结构5.8编译预处理第5章程序结构5.1作用域与可见性5.1.1作用域作用域是一个标识符在程序正文中有效的区域。C++的作用域有函数原型作用域、块作用域(亦称局部作用域)和文件作用域。1.函数原型作用域函数原型作用域,始于函数原型声明的左括号“(”,结束于右括号“)”。例如,有如下函数声明:doubleArea(doublelength,doublewidth);由于形参length和width只在括号之内有效,在程序的其它地方无法引用这个标识符,如果要引用,必须重新定义。第5章程序结构2.块作用域块作用域又称局部作用域。当标识符的声明出现在由一对花括号“{}”所括起来的程序(块)内时,则此块中声明的标识符的作用域从声明处开始,一直到块结束的花括号为止。为了理解块作用域,我们来看一个例子:#includeiostream.hvoidmain(){intn;for(inti=0;i5;i++){intm;if(i%2)//m作用域i作用域n作用域n++;}m=n/2;//错误,m未定义n=i;}第5章程序结构3.文件作用域具有文件作用域的标识符是在所有函数定义之外声明的,其作用域从声明点开始,一直延伸至文件尾。一般情况下,程序中所声明的全局变量都具有文件作用域,它们在整个文件中都有效。【例5-1】文件作用域例题。#includeiostream.hintk;voidmain(){k=5;{k++;}coutk=kendl;}程序运行结果为k=6这个例子中,在主函数之前声明的变量k具有文件作用域,它的有效作用范围是整个源代码文件。第5章程序结构5.1.2可见性作用域指的是标识符有效的范围,可见性从另一个角度表现标识符的有效范围。标识符的可见性是指在程序的某个位置,该标识符可以被有效地引用,因此,形象地称为可见性。可见性遵循的一般规则如下:①标识符在引用前必须先声明。②在互相没有包含关系的不同作用域中声明同名的标识符时,两标识符互不影响。③如果在两个或多个具有包含关系的作用域中声明了同名标识符,则外层标识符在内层不可见。第5章程序结构【例5-2】具有包含关系的作用域中可见性例题。#includeiostream.hintk;voidmain(){k=10;{intk=5;coutk=kendl;}coutk=kendl;}程序运行结果为k=5k=10在这个例子中,主函数之前声明的变量k具有文件作用域,它的有效作用范围是整个源代码文件;主函数内声明的变量k具有块作用域,它的作用范围在内层的花括号内,k的块作用域被完全包含在k的文件作用域中。第5章程序结构5.2生存期5.2.1静态生存期静态生存期的变量只要程序一开始运行,它就存在,直到程序运行结束,此变量的生存期也就结束了。具有文件作用域的变量具有静态生存期。如果要在函数内部的块作用域中声明具有静态生存期的变量,则要使用关键字static。具有静态生存期的变量在固定的数据区域内分配空间。如果具有静态生存期的变量未初始化,则自动初始化为0。全局变量、静态全局变量和静态局部变量都具有静态生存期。第5章程序结构5.2.2局部生存期在块作用域中声明的变量具有局部生存期。此生存期诞生于声明点,而终止于其作用域的结束处。因此,具有局部生存期的变量都具有块作用域。但反之则不然,一般具有块作用域的变量都具有局部生存期,但当在块作用域内将变量说明为静态变量时,该变量则具有静态生存期。例如:voidmain(){staticintk;//...}5.2.3动态生存期动态生存期由程序中特定的函数(malloc()和free())调用或由操作符(new和delete)创建和释放,这部分内容将在第6章中介绍。具有动态生存期的变量在内存的堆区分配空间。第5章程序结构5.3局部变量和全局变量5.3.1局部变量局部变量具有局部作用域。因此,在不同函数体内的局部变量是互相不可见的。局部变量包括自动(auto)变量、内部静态(static)变量和函数参数。自动变量是在函数体或分程序内声明的变量,具有块作用域。声明时,变量前可以加auto,也可以不加,程序中没有特别说明的变量都是自动变量。函数参数实质上就是自动变量。局部变量能够在调用和被调用函数之间通过参数进行数据传递。第5章程序结构5.3.2全局变量全局变量具有文件作用域。在整个程序中,除了在定义有同名局部变量的块中之外,其它地方都可以直接访问全局变量。将数据存放在全局变量中,不同的函数在不同的地方对同一个全局变量进行访问,实现了这些函数之间的数据共享。请看下面的程序:#includeiostream.hintn;voidf(){n=5;}voidg(){coutn=nendl;}voidmain(){f();g();}程序运行结果为n=5这样的共享方法,使用相当方便,但是副作用也不可低估。第5章程序结构【例5-3】局部变量和全局变量例题。#includeiostream.hinti=1;voidother(void);voidmain(){staticinta;//intb=-10;intc=0;cout----main----endl;第5章程序结构couti=ia=ab=bc=cendl;c=c+8;other();cout----main----endl;couti=ia=ab=bc=cendl;other();}voidother(void){staticinta=1;//局部静态变量,但是作用域是全局的第5章程序结构staticintb;intc=5;i=i+2;a=a+3;c=c+5;cout----other----endl;couti=ia=ab=bc=cendl;b=a;}程序运行结果为----main----i=1a=0b=-10c=0----other----i=3a=4b=0c=10----main----i=3a=0b=-10c=8----other----i=5a=7b=4c=10第5章程序结构【例5-4】局部对象和全局对象例题。#includeiostream.hclassClock//时钟类声明{private:intHour,Minute,Second;public:Clock()//构造函数{第5章程序结构Hour=0;Minute=0;Second=0;}voidSetTime(intNewH,intNewM,intNewS);//设置时间函数voidShowTime();~Clock(){}};voidClock::SetTime(intNewH,intNewM,intNewS){第5章程序结构Hour=NewH;Minute=NewM;Second=NewS;}voidClock::ShowTime(){coutHour:Minute:Secondendl;}ClockglobClock;//声明具有静态生存期、文件作用域的对象globClockvoidmain(){第5章程序结构coutFirsttimeoutput:endl;globClock.ShowTime();globClock.SetTime(10,30,45);Clockmyclock(globClock);//声明具有块作用域的对象myclockcoutSecondtimeoutput:endl;myclock.ShowTime();}第5章程序结构程序运行结果为Firsttimeoutput:0:0:0Secondtimeoutput:10:30:45程序中包含了具有各种作用域的变量和对象。其时钟类声明中,函数成员SetTime的三个形参具有函数原型作用域,对象globClock具有文件作用域,对象myclock具有块作用域。在主函数中,这些变量、对象及其公有成员都是可见的。第5章程序结构5.4静态成员静态成员用于解决同一个类的不同对象之间的数据和函数的共享问题。例如,我们可以抽象出学生的共性,设计如下的学生类:classStudent{private:intStudentNo;intID;char*name;//字符指针,将在第6章介绍//…};如果需要统计学生总数,这个数据存放在什么地方呢?若以类外的变量来存储总数,不能实现数据的隐藏。若在类中增加一个数据成员用以存放总数,必然在每一个对象中都存储一个副本,不仅冗余,而且每个对象分别维护一个“总数”,势必造成数据的不一致。因此,比较理想的方案是使类的所有对象共同拥有一个用于存放总数的数据成员,这就是下面要介绍的静态数据成员。第5章程序结构5.4.1静态数据成员类的普通数据成员在类的每一个对象中都拥有一个拷贝,也就是说,每个对象的同名数据成员可以分别存储不同的数值,这也是每个对象拥有自身特征的保证。而静态数据成员是类的数据成员的一种特例。每个类只有一个静态数据成员拷贝,它由该类的所有对象共同维护和使用,从而实现了同一个类的不同对象之间的数据共享。静态数据成员具有静态生存期。在静态数据成员的声明和使用时,应注意以下几点:第5章程序结构①静态数据成员声明时,应在前面加static关键字来说明。例如:staticintn②静态数据成员必须初始化,并且一定要在类外进行。其初始化的形式如下:类型标识符类名::静态数据成员名=值例如:intpoint::n=0;③静态数据成员属于类,而不属于任何一个对象,所以,在类外只能通过类名对它进行引用。静态数据成员引用的一般形式如下:类名::静态数据成员名;静态数据成员同一般数据成员一样要服从访问控制限制。当静态数据成员被声明为私有成员时,只能在类内直接引用它,在类外无法引用。但当静态数据成员被声明为公有成员或保护成员时,可以在类外通过类名对它进行引用。第5章程序结构#includeiostream.hclassTest{private:intk;public:staticintn;//静态数据成员Test(intkk){k=kk;n++;}voidDisplay(){coutn=nk=kendl;}};intTest::n=0;//静态数据成员初始化voidmain(){Testt1(10);t1.Display();Testt2(20);t2.Display();Test::n++;t2.Display();}【例5-5】含有静态数据成员例题。//test_static.cpp第5章程序结构5.4.2静态函数成员所谓静态函数成员,就是使用static关键字声明的函数成员。同静态数据成员一样,静态函数成员也属于整个类,由同一个类的所有对象共同维护,为这些对象所共享。静态函数成员可以直接引用该类的静态数据,而不能直接引用非静态数据成员,如果要引用,必须通过参数传递的方式得到对象名,然后再通过对象名来引用。作为成员函数,静态函数成员的访问属性可以受到类的严格控制。对于公有的静态函数成员,可以通过类名或对象名来调用;而对于一般的非静态函数成员,只能通过对象名来调用。第5章程序结构【例5-6】使用静态函数成员例题。//test_staticFunction.cpp#includeiostream.hclasspoint{private:intx,y;staticintcountP;//静态数据成员public:point(intxx=0,intyy=0){x=xx;y=yy;countP++;}point(point&p);//拷贝构造函数intget_x(){returnx;}intget_y(){returny;}staticvoidget_