很多人给出了基本答案,但没有人指出,在C++中const
默认static
的namespace
水平(有的给出了错误信息).请参阅C++ 98标准第3.5.3节.
首先是一些背景:
翻译单元:预处理器(递归地)之后的源文件包括其所有包含文件.
静态链接:符号仅在其翻译单元中可用.
外部链接:其他翻译单元可以使用符号.
namespace
水平这包括全局命名空间aka全局变量.
static const int sci = 0; // sci is explicitly static const int ci = 1; // ci is implicitly static extern const int eci = 2; // eci is explicitly extern extern int ei = 3; // ei is explicitly extern int i = 4; // i is implicitly extern static int si = 5; // si is explicitly static
static
表示在函数调用之间保持该值.
函数static
变量的语义类似于全局变量,因为它们驻留在程序的数据段(而不是堆栈或堆)中,有关变量生命周期的更多详细信息,请参阅此问题static
.
class
水平static
表示该值在类的所有实例之间共享,并且const
意味着它不会更改.
很多人给出了基本答案,但没有人指出,在C++中const
默认static
的namespace
水平(有的给出了错误信息).请参阅C++ 98标准第3.5.3节.
首先是一些背景:
翻译单元:预处理器(递归地)之后的源文件包括其所有包含文件.
静态链接:符号仅在其翻译单元中可用.
外部链接:其他翻译单元可以使用符号.
namespace
水平这包括全局命名空间aka全局变量.
static const int sci = 0; // sci is explicitly static const int ci = 1; // ci is implicitly static extern const int eci = 2; // eci is explicitly extern extern int ei = 3; // ei is explicitly extern int i = 4; // i is implicitly extern static int si = 5; // si is explicitly static
static
表示在函数调用之间保持该值.
函数static
变量的语义类似于全局变量,因为它们驻留在程序的数据段(而不是堆栈或堆)中,有关变量生命周期的更多详细信息,请参阅此问题static
.
class
水平static
表示该值在类的所有实例之间共享,并且const
意味着它不会更改.
它在C和C++中都有用.
正如您所猜测的那样,该static
部分将其范围限制在该编译单元.它还提供静态初始化.const
只是告诉编译器不要让任何人修改它.根据体系结构,此变量可以放在数据段或bss段中,也可以在内存中标记为只读.
这就是C如何处理这些变量(或C++如何处理命名空间变量).在C++中,标记的成员static
由给定类的所有实例共享.它是否为私有不会影响一个变量由多个实例共享的事实.如果const
有任何代码会尝试修改它,那么就会在那里发出警告.
如果它是严格私有的,那么该类的每个实例都将获得自己的版本(尽管优化器).
这行代码实际上可以出现在几个不同的上下文中,并且尽管它的行为大致相同,但存在小的差异.
命名空间范围// foo.h static const int i = 0;
' i
'将在包含标题的每个翻译单元中显示.但是,除非您实际使用对象的地址(例如.' &i
'),否则我很确定编译器会将' i
'简单地视为类型安全0
.如果两个以上的翻译单元采用' &i
',则每个翻译单元的地址将不同.
// foo.cc static const int i = 0;
' i
'具有内部联系,因此不能从该翻译单元外部引用.但是,除非您使用其地址,否则它很可能被视为类型安全0
.
值得指出的一点是,以下声明:
const int i1 = 0;
是完全相同一样static const int i = 0
.声明为const
和未显式声明的名称空间中的变量extern
是隐式静态的.如果您考虑这一点,C++委员会的目的是允许const
在头文件中声明变量而不总是需要static
关键字以避免破坏ODR.
class A { public: static const int i = 0; };
在上面的示例中,标准明确指定i
如果不需要其地址则不需要定义' '.换句话说,如果您只使用' i
'作为类型安全0,那么编译器将不会定义它.类和命名空间版本之间的一个区别是,' i
'(如果在两个以上的翻译单元中使用)的地址对于类成员将是相同的.在使用地址的地方,您必须有一个定义:
// a.h class A { public: static const int i = 0; }; // a.cc #include "a.h" const int A::i; // Definition so that we can take the address
这是一个小空间优化.
当你说
const int foo = 42;
您没有定义常量,而是创建只读变量.编译器足够智能,只要看到foo就会使用42,但它也会为初始化数据区域分配空间.这样做是因为,如所定义的,foo具有外部链接.另一个编译单位可以说:
extern const int foo;
获取其价值的访问权限.这不是一个好习惯,因为编译单元不知道foo的价值是什么.它只知道它是一个const int,并且必须在使用它时从内存重新加载它.
现在,通过声明它是静态的:
static const int foo = 42;
编译器可以进行通常的优化,但它也可以说"嘿,这个编译单元之外的任何人都看不到foo,我知道它总是42,所以不需要为它分配任何空间."
我还应该注意,在C++中,防止名称转义当前编译单元的首选方法是使用匿名命名空间:
namespace { const int foo = 42; // same as static definition above }
它缺少'int'.它应该是:
const static int foo = 42;
在C和C++中,它声明了一个整数常量,其本地文件范围为42.
为什么42?如果你还不知道(很难相信你不知道),那就是对生命,宇宙和万物的回答.