在C和C ++中,“const static”是什么意思?

const static int foo = 42; 

我在StackOverflow的一些代码中看到了这一点,我无法弄清楚它在做什么。 然后我在其他论坛上看到一些混淆的答案。 我最好的猜测是它在C中用来隐藏其他模块的常量foo 。 它是否正确? 如果是这样,为什么任何人都可以在C ++上下文中使用它,你可以把它变成private

它在C和C ++中都有使用。

正如你猜测的那样, static部分将其范围限制在编译单元中 。 它也提供了静态初始化。 const只是告诉编译器不要让任何人修改它。 这个variables要么根据体系结构放在数据段中,要么放在内存中标记为只读。

所有这些都是C如何处理这些variables(或者C ++如何处理命名空间variables)。 在C ++中,标记为static的成员由给定类的所有实例共享。 无论是否私有都不会影响一个variables被多个实例共享的事实。 如果有任何代码会尝试修改,那么在那里有const会警告你。

如果它是严格私有的,那么每个类的实例都会得到它自己的版本(优化器尽pipe)。

很多人都给出了基本的答案,但没有人指出,在C ++中, constnamespace级别默认为static (有些给出了错误的信息)。 请参阅C ++ 98标准部分3.5.3。

首先是一些背景:

翻译单元:预处理器(recursion)之后的源文件包括其所有包含文件。

静态链接:符号仅在其翻译单元中可用。

外部链接:其他翻译单元提供了一个符号。

namespace级别

这包括全局名称空间又名全局variables

 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 

在function级别

static意味着函数调用之间的值保持不变。
函数staticvariables的语义类似于全局variables,因为它们驻留在程序的数据段 (而不是堆栈或堆)中,请参阅此问题以获取有关staticvariables生命周期的更多详细信息。

class

static意味着该值在类的所有实例之间共享,并且const意味着它不会改变。

这行代码实际上可能出现在几个不同的上下文中,并且它们的行为大致相同,但有一些细微的差别。

命名空间范围

 // foo.h static const int i = 0; 

包含标题的每个翻译单元中都会显示“ i ”。 但是,除非实际使用对象的地址(例如' &i '),否则我很确定编译器将“ i ”视为types安全0 。 如果再有两个翻译单元采用“ &i ”,则每个翻译单元的地址将会不同。

 // foo.cc static const int i = 0; 

' i '有内部联系,所以不能从这个翻译单位以外引用。 但是,除非你使用地址,否则它很可能被视为types安全的0

值得指出的一点是,下面的声明是:

 const int i1 = 0; 

static const int i = 0 完全一样。 用const声明的一个名字空间中的variables并没有用extern显式声明,而是隐含的静态的。 如果你想到这一点,C ++委员会的目的是允许在头文件中声明constvariables,而不需要static关键字来避免破坏ODR。

class级范围

 class A { public: static const int i = 0; }; 

在上面的例子中,标准明确规定如果地址不是必需的,“ i ”不需要定义。 换句话说,如果只使用' i '作为types安全的0,那么编译器将不会定义它。 类和名称空间版本之间的一个区别是“ i ”(如果在两个或更多的翻译单元中使用)的地址对于类成员将是相同的。 在使用地址的地方,你必须有一个定义:

 // ah class A { public: static const int i = 0; }; // a.cc #include "ah" const int A::i; // Definition so that we can take the address 

这是一个小空间优化。

当你说

 const int foo = 42; 

你没有定义一个常量,而是创build一个只读variables。 编译器足够聪明,只要看到foo就可以使用它,但是它也会在初始化的数据区域中为它分配空间。 这样做是因为,如所定义的,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 } 

它缺less一个'int'。 它应该是:

 const static int foo = 42; 

在C和C ++中,它声明一个整型常量,其值为42。

为什么42? 如果你还不知道(很难相信你不知道),那就是对生命回应,宇宙和万物回应

在C ++中,

 static const int foo = 42; 

是定义和使用常量的首选方法。 即使用这个而不是

 #define foo 42 

因为它不会颠覆型号安全系统。

这是全局常量只有在编译模块(.cpp文件)中才可见/可访问。 BTW使用静态为此目的已被弃用。 更好地使用匿名命名空间和枚举:

 namespace { enum { foo = 42 }; } 

所有的好的答案,我想添加一个小细节:

如果您编写插件(例如,由CAD系统加载的DLL或.so库),那么static就是一个避免名称冲突的生命保护程序,如下所示:

  1. CAD系统加载一个插件A,它有一个“const int foo = 42;” 在里面。
  2. 系统加载一个插件B,其中包含“const int foo = 23;” 在里面。
  3. 结果,插件B将为foo使用值42,因为插件加载器会意识到,已经有一个具有外部链接的“foo”。

更糟糕的是:根据编译器优化,插件加载机制等,第3步可能会有所不同。

我有两个插件中的两个辅助函数(相同的名称,不同的行为)曾经有过这个问题。 宣布他们静态解决了这个问题。

是的,它从其他模块隐藏了一个模块的variables。 在C ++中,当我不需要/需要更改一个会触发不必要的重build其他文件的.h文件时,我会使用它。 另外,我把静态的第一:

 static const int foo = 42; 

而且,根据它的用途,编译器甚至不会为它分配存储空间,只是“内联”它所使用的值。 如果没有静态的,编译器不能假定它没有在其他地方使用,也不能内联。

使其保持私密仍然意味着它出现在标题中。 我倾向于使用“最弱”的方式。 请参阅Scott Meyers的这篇经典文章: http : //www.ddj.com/cpp/184401197 (这是关于函数,但也可以在这里应用)。

根据C99 / GNU99规范:

  • static

    • 是存储类说明符

    • 文件级作用域的对象默认具有外部连接

    • 具有静态说明符的文件级范围的对象具有内部链接
  • const

    • 是types限定符(是types的一部分)

    • 关键字应用到即时左侧实例 – 即

      • MyObj const * myVar; – 不合格的指向const限定对象types的指针

      • MyObj * const myVar; – const限定指向非限定对象types的指针

    • 最左边的用法 – 应用于对象types,不可变

      • const MyObj * myVar; – 不合格的指向const限定对象types的指针

从而:

static NSString * const myVar; – 不变的指向内部连接的不可变string的指针。

缺lessstatic关键字将使全局variables名称可能导致应用程序中的名称冲突。