http://www.cnblogs.com/wolf-lifeng/p/3156936.html
2.3全局作用域
2.3.1概述
全局作用域是最大的名字空间作用域,不同于用户自定义的名字空间作用域,全局作用域不需要显示地定义,它天然存在于C++程序中。全局作用域是一个最外层的容器,是所有作用域的父作用域。在全局作用域中,可以定义其他的名字空间,类型,函数,变量,模版等。
在全局作用域中定义的函数是全局函数,在全局作用域中定义的变量是全局对象。全局函数和全局对象在整个全局作用域及其子作用域中有效,它们的生命周期贯穿于整个程序的运行。从定义它们开始直到整个程序运行结束。
2.3.2一次定义规则
2.3.2.1声明和定义
变量可以被声明多次,但只能被定义一次。声明和定义是两个不同的概念。
在变量定义的时候,除了向程序表明变量的类型和名称外,还要为变量分配存储空间以及进行初始化操作。在定义全局变量的时候,如果没有为变量赋初值,那么系统将执行默认的初始化工作,如:整型变量赋值0,字符串变量赋空值等。全局变量的值存储在程序的全局存储区,在全局存储区中还存储着常量的值,以及静态变量的值。
声明操作是定义操作的子集,在声明变量的时候,仅仅向程序表明变量的类型和名称,它不会为变量分配存储空间,因此也不会有初始化工作。
全局变量和全局函数的声明和定义的格式如下:
//声明 Void myFunction(int);//声明一个函数,指定该函数的名称,返回值,以及参数列表。 Extern void myFunction(int);//在声明函数的时候,extern关键字可选,加上该关键字表示显//示声明。 Extern in myVar; //声明一个变量。必须加关键字extern。表明此处仅仅是声明,而定义在其//他地方 Extern const char* const myConstVar;//声明一个指向常量的常量指针。 //定义 Void myFunction(int Para)//函数定义,包括函数的声明部分和函数体 { //函数的功能代码 } Int myVar = 10;//变量定义 const char* const myConstVar = new char[10];//常量定义。 Extern int myIntVar = 50;//变量的定义。 |
在声明全局变量的时候,必须加关键字extern,并且不能对变量进行初始化。如果对变量执行了初始化操作,即使在前面加了extern关键字,也会被认为是变量的定义,而不是变量的声明。
声明和定义的关系是:定义也是声明,定义包含了声明,但声明仅仅是定义的一个子步骤。有的时候,声明和定义可以合在一起进行,如局部变量的声明和定义,而在某种情况下,声明和定义是需要分开进行的。当一个变量需要在多个文件中被使用的时候,就需要将声明和定义分开(一般为全局变量);否则使用一个定义语句同时完成变量的声明和定义即可(一般为局部变量)。
2.3.2.2合理的文件组织形式
一个变量可以被声明多次,但它只能被定义一次。在这里存在一个法则,一次定义法则:全局对象和全局函数或者只有一次定义,或者在一个程序中有多个完全相同的定义(内两函数和全局常量的情况)。
为了遵守一次定义法则,需要合理的文件组织形式。在一般情况下,全局作用域中定义的全局变量和全局函数都需要在多个文件中被使用。在这种情况下,最合理的文件组织形式就是:“头文件+cpp文件”的形式。具体的过程描述如下:
- 在头文件中声明全局变量和全局函数;
- 在头文件中定义内联函数,以及在编译时可以确定值的常量;
- 在源文件(.cpp文件)中引入头文件,然后定义这些在头文件中声明的变量和函数;
- 在需要使用这些全局变量和全局函数的文件中引入声明了这些全局变量和全局函数的头文件,开始使用这些全局变量和全局函数。
头文件是用来声明而不是定义函数和变量的,而源文件则是用来实现变量和函数定义的。头文件中一般包含类的定义,枚举的定义,符号常量的定义,内联函数的定义,extern变量的声明,函数的声明,typedef声明。如果将变量定义到头文件中,那么当其他文件引入该头文件的时候,就会认为该对头文件中的变量又执行了一次定义。根据一次定义法则,这是不允许的。但是也有三个例外,类,内联函数,在编译时值可以确定的符号常量是可以在头文件中定义的。
按照头文件的方式处理全局变量和全局函数的具体代码的格式如下:
//头文件中实现函数和变量的声明 --------------------A.h---------------------------------- Extern int myIntVar;//声明一个变量,必须以关键字extern开头。 Void myFunction(int);//声明一个函数,指定函数名称,返回值,参数列表。 Const double mydlVar = 3.14;//定义一个常量。该常量的值在编译时可知。 Inline double GetdlVar()//定义内联函数 { Return mydlVar; } Extern const char* const myConstVar;//声明全局常量,该常量的值在编译时不可知。 //源文件中实现变量和函数的定义。 ---------------------------A.cpp----------------------------------- #include “A.h” Int myIntVar = 100;//定义变量 Void myFunction(int Para) { //函数的实现代码 } const char* const myConstVar = new char[100];//常量的值在运行时才能确定,因此在这里实现它的定义,而在头文件中仅仅是声明。 //在其他文件中使用定义的全局变量和全局函数,首先引入该头文件。 ----------------------------other.cpp-------------------------------- #include “A.h” --以下是具体使用。 |
2.3.3全局作用域的成员
2.3.3.1普通变量
在C++中,编译一个程序可以划分为如下的阶段:
- 预处理阶段,处理#include命令。
- 编译阶段,
- 链接阶段。
如果在一个源文件中使用了“#Include”命令,将一个头文件引入到该源文件中。那么在编译器执行编译之前,会首先执行该预处理命令。即:将头文件中所包含的代码全部合并的目标源文件中,合并后的文件将作为一个文件存在。也就是说,编译器在执行编译的时候,只会看到源文件,头文件在编译阶段是不可见的。
那么,假设有如下的实现方式:在头文件A.h中,实现了一个变量的定义(不是声明),如:“int a = 10;”。每当该头文件被其他源文件引用一次以后(#include “A.h”),那么编译器就会认为对该变量执行了一次定义。如果该头文件被引用两次以上,就会发生重定义错误。这是违反一次定义法则的。
基于以上原因,对于普通的全局变量,正确的使用方法是:首先在头文件中使用extern关键字实现该全局变量的声明;然后在源文件中引入该头文件,并且实现该全局变量的定义;最后,在使用该全局变量的源文件中引入声明了该全局变量的头文件,然后使用之。
对于普通的全局变量,只能使用此方法处理。
2.3.3.2 Const常量
在全局作用域中定义的常量只具有文件作用域。也就是说,在全局作用域中定义的常量只在定义它的文件内有效,它不会影响到其他文件中定义的同名常量。举例如下:
--------------------------A.h---------------------------- Const int myIntVar = 100;//在头文件中定义了一个常量 ------------------------B.cpp---------------------------- #include “A.h” Int a = myIntVar; ------------------------C.cpp---------------------------- #include “A.h” Int b = myIntVar; |
示例1
上面的代码能够正确运行。虽然“A.h”头文件被引用了多次,但是由于全局常量只具有文件作用域,所以在B.cpp文件中定义的全局常量“myIntVar”与在C.cpp文件中定义的全局常量“myIntVar”互不影响。
这是使用全局常量的一种方式,只要该全局常量的值在编译时刻是确定的。在上面的示例中,全局常量“myIntVar”不会被正真地存储到全局区。在编译阶段,编译器会用该全局常量的值去替换使用“myIntVar”名称的地方。因此,要求在使用全局常量的地方,该全局常量的定义是可见的。示例1中的方式能够满足这种要求。当对符号常量使用extern关键字,或者取符号常量的地址的时候,编译器会为符号常量分配存储空间,否则只是执行编译时刻的替换。
现在考虑如下问题,如果全局常量的值在编译时刻不确定呢?比如:使用new操作符定义一个全局常量,那么情况会如何?举例如下:
-------------------A.h--------------------- Char* const pChar = new char[100];//定义一个常量指针。注意与:const char* pChar = new char[100]的区别。 -------------------B.cpp------------------ #include “A.h” ------------------C.cpp-------------------- #include “A.h” |
示例2
每当在源文件中引用一次该头文件,那么就会执行一次内存分配。这显然与我们期望的结果不符。我们想要的是一个常量指针,该指针指向一个字符串。然后,我们可以在其他多个文件中使用该常量指针。
可以使用关键字extern打破const常量的文件作用域,使之具有全局作用域。具体的作法举例如下:
-------------------------A.h--------------------- Extern char* const pChar;//声明全局常量,该头文件可以被其他文件引用 ------------------------A.cpp-------------------- #include “A.h” Char* const pChar = new char[100];//定义常量,编译器分配存储空间 Memset(pChar,’ |