const对象仅在文件内有效的问题
const对象仅在文件内有效的问题
变量和声明的关系
(在正式讲为什么默认状态下,const对象仅在文件内有效之前,我们得先了解变量和声明的关系。)
C++支持分离式编译,可以将程序拆成多个文件,每个文件可以被独立编译。但需要在文件间共享代码,一个文件可能需要使用另一个文件定义的变量。比如std::cout定义于标准库,我们却一直在自己的程序里使用。
为了支持分离式编译,C++把声明和定义区分开来。
声明使得名字被程序所知,一个文件如果想使用别处定义的名字,就必须包含对该名字的声明。
定义则负责创建于名字关联的实体。定义申请存储空间,也可能会为变量赋一个初始值。
如果想声明一个变量而非定义它,就在变量名前添加关键字extern,而且不要显式地初始化变量,因为显式初始化了的声明就会变为定义(让extern失效了):
extern int i;//声明
int i;//定义
extern int i = 1;//定义
而如果是在函数体内,试图去初始化一个由extern关键字标记的变量,会引发错误:
如果执行这段程序:
引发错误原因:
1.作用域限制
函数体内部定义的变量具有局部作用域,只在该函数执行期间存在。而用extern声明的变量通常具有外部链接,其作用域通常是整个程序或多个文件。
在函数内部初始化一个extern变量,相当于试图在局部作用域内重新定义一个原本应该在外部作用域中定义的变量,这与extern的语义相冲突。
2.链接阶段的问题
当编译器遇到extern声明时,它只是知道有这样一个变量存在于其他地方,但并不知道其具体的值或存储位置。只有在链接阶段,链接器才会将各个编译单元中的符号进行解析和绑定。
如果在函数体内部初始化extern变量,编译器无法确定这个初始化值在整个程序中的唯一性和正确性,因为可能有多个地方都在使用这个变量,并且可能有不同的初始化值,这会导致链接错误。
总之,extern关键字的目的是提供一种机制来访问在其他地方定义的变量,而不是在局部作用域内进行初始化。如果需要初始化一个变量,应该在合适的全局或文件作用域中进行定义和初始化。
变量能且只能被定义一次,但是可以被多次声明。
如果要在多个文件里使用同一个变量,就必须将声明和定义分离。此时,变量的定义必须出现在且只能出现在一个文件中,而其他用到该变量的文件必须对其进行声明,却绝对不能重复定义。
矛盾和解决方案
const对象一旦创建后就不能再改变,所以const对象必须初始化。然而其初始化的方式有两种:
const int i = 1;//编译时初始化
const int i = get_size();//运行时初始化
当我们使用编译时初始化时,编译器将在编译过程中把用到该变量的地方都替换成对应的值。
为了替换,编译器必须知道变量的初始值。如果程序包含多个文件,则每个用到了const对象的文件都必须含有对它的定义(因为各个文件独立编译)。
为了能在每个有这种需要的文件中都有一份定义,同时避免对同一const对象的重复定义,默认情况下,const对象就被设定为仅在文件内部有效。当多个文件中同时出现了同名的const变量时,其实等同于在不同文件中分别定义了独立的变量。
特殊情况
Somehow有这样一种const变量,它的初始值不是一个常量表达式,但又需要在多文件间共享,所以我们就想要只在一个文件中定义这个const变量,而不是每个文件分别生成独立的变量。
解决的办法是,对于const变量不管是声明还是定义都添加extern关键字,这样只需定义一次就可以了:
//file_1.cc定义并初始化了一个常量,该常量能被其他文件访问
extern const int bufsize = fcn();
//file_1.h 头文件
extern const int bufsize;//与file_1.cc里定义的是同一个
就像这样使用。
那么,本文到此结束,11月快乐。