在C ++中定义全局常量

时间:2010-02-15 20:41:41

标签: c++

我想在C ++中定义一个常量,以便在几个源文件中可见。 我可以想象以下方法在头文件中定义它:

  1. #define GLOBAL_CONST_VAR 0xFF
  2. int GLOBAL_CONST_VAR = 0xFF;
  3. 某些功能会恢复该值(例如int get_GLOBAL_CONST_VAR()
  4. enum { GLOBAL_CONST_VAR = 0xFF; }
  5. const int GLOBAL_CONST_VAR = 0xFF;
  6. extern const int GLOBAL_CONST_VAR; 并在一个源文件const int GLOBAL_CONST_VAR = 0xFF;
  7. 选项(1) - 绝对不是您想要使用的选项

    选项(2) - 使用头文件

    定义每个目标文件中的变量实例

    选项(3) - 在大多数情况下,IMO过度杀人

    选项(4) - 在许多情况下可能不好,因为枚举没有具体类型(C ++ 0X将增加定义类型的可能性)

    所以在大多数情况下我需要在(5)和(6)之间进行选择。 我的问题:

    1. 您更喜欢(5)或(6)?
    2. 为什么(5)没问题,而(2)不是?

10 个答案:

答案 0 :(得分:64)

绝对使用选项5 - 它是类型安全的并且允许编译器进行优化(不要获取该变量的地址:)如果它在标题中 - 将其粘贴到命名空间中以避免污染全局范围:

// header.hpp
namespace constants
{
    const int GLOBAL_CONST_VAR = 0xFF;
    // ... other related constants

} // namespace constants

// source.cpp - use it
#include <header.hpp>
int value = constants::GLOBAL_CONST_VAR;

答案 1 :(得分:29)

(5)准确说出你想说的话。此外,它允许编译器在大多数情况下优化它。 (6)另一方面不会让编译器对它进行优化,因为编译器不知道你最终是否会改变它。

答案 2 :(得分:22)

(5)比(6)“更好”,因为它将GLOBAL_CONST_VAR定义为所有翻译单元中的积分常数表达式(ICE)。例如,您可以在所有翻译单元中将其用作数组大小和案例标签。在(6)GLOBAL_CONST_VAR的情况下,{I}仅在该翻译单元中定义ICE,并且仅在定义点之后。在其他翻译单元中,它不能用作ICE。

但请注意,(5)提供GLOBAL_CONST_VAR内部链接,这意味着GLOBAL_CONST_VAR的“地址标识”在每个翻译单元中都会有所不同,即&GLOBAL_CONST_VAR将在每个翻译单元中为您提供不同的指针值。在大多数用例中,这并不重要,但如果你需要一个具有一致全局“地址标识”的常量对象,那么你必须使用(6),牺牲常量的ICE-ness。过程

此外,当常量的ICE不是问题(不是整数类型)并且类型的大小变大(不是标量类型)时,那么(6)通常变得比(5)更好的方法)。

(2)不正常,因为默认情况下(2)中的GLOBAL_CONST_VAR具有外部链接。如果你把它放在头文件中,你通常会得到GLOBAL_CONST_VAR的多个定义,这是一个错误。默认情况下,C ++中的const个对象具有内部链接,这就是为什么(5)有效(这就是为什么,正如我上面所说,你在每个翻译单元中得到一个单独的,独立的GLOBAL_CONST_VAR。) / p>

答案 3 :(得分:5)

如果它将是一个常数,那么你应该将它标记为常数 - 这就是为什么2在我看来是不好的。

编译器可以使用值的const特性来扩展一些数学,以及使用该值的其他操作。

5和6之间的选择 - hmm; 5对我来说感觉更好。

在6)中,该值与其声明不必要地脱离。

我通常会有一个或多个这些标题只定义其中的常量等,然后没有其他“聪明”的东西 - 很好的轻量级标题,可以很容易地包含在任何地方。

答案 4 :(得分:4)

回答你的第二个问题:

(2)是违法的,因为它违反了One Definition Rule。它在包含它的每个文件中定义GLOBAL_CONST_VAR,即不止一次。 (5)是合法的,因为它不受一种定义规则的约束。每个GLOBAL_CONST_VAR都是一个单独的定义,包含在该文件的本地。当然,所有这些定义都具有相同的名称和价值,但它们的地址可能不同。

答案 5 :(得分:4)

如果您使用的是C ++ 11或更高版本,请尝试使用编译时常量:

lvresize

答案 6 :(得分:2)

const int GLOBAL_CONST_VAR = 0xFF;

因为它是一个常数!

答案 7 :(得分:1)

#define GLOBAL_CONST_VAR 0xFF // this is C code not C++
int GLOBAL_CONST_VAR = 0xFF; // it is not constant and maybe not compilled
Some function returing the value (e.g. int get_LOBAL_CONST_VAR()) // maybe but exists better desision
enum { LOBAL_CONST_VAR = 0xFF; } // not needed, endeed, for only one constant (enum elms is a simple int, but with secial enumeration)
const int GLOBAL_CONST_VAR = 0xFF; // it is the best
extern const int GLOBAL_CONST_VAR; //some compiller doesn't understand this

答案 8 :(得分:1)

这取决于您的要求。 (5)对于大多数正常使用来说是最好的,但是经常导致在每个目标文件中不断占用存储空间。 (6)可以在重要的情况下解决这个问题。

(4)也是一个不错的选择,如果你的优先级是保证永远不会分配存储空间,但它只适用于整数常量。

答案 9 :(得分:0)

C ++ 17 inline变量

这项令人敬畏的C ++ 17功能使我们能够:

main.cpp

#include <cassert>

#include "notmain.hpp"

int main() {
    // Both files see the same memory address.
    assert(&notmain_i == notmain_func());
    assert(notmain_i == 42);
}

notmain.hpp

#ifndef NOTMAIN_HPP
#define NOTMAIN_HPP

inline constexpr int notmain_i = 42;

const int* notmain_func();

#endif

notmain.cpp

#include "notmain.hpp"

const int* notmain_func() {
    return &notmain_i;
}

编译并运行:

g++ -c -o notmain.o -std=c++17 -Wall -Wextra -pedantic notmain.cpp
g++ -c -o main.o -std=c++17 -Wall -Wextra -pedantic main.cpp
g++ -o main -std=c++17 -Wall -Wextra -pedantic main.o notmain.o
./main

GitHub upstream

另请参阅:How do inline variables work?

内联变量的C ++标准

C ++标准保证地址相同。 C++17 N4659 standard draft 10.1.6“内联说明符”:

  

6具有外部链接的内联函数或变量在所有翻译单元中应具有相同的地址。

cppreference https://en.cppreference.com/w/cpp/language/inline解释说,如果未提供static,则它具有外部链接。

内联变量实现

我们可以观察到它是如何实现的:

nm main.o notmain.o

其中包含:

main.o:
                 U _GLOBAL_OFFSET_TABLE_
                 U _Z12notmain_funcv
0000000000000028 r _ZZ4mainE19__PRETTY_FUNCTION__
                 U __assert_fail
0000000000000000 T main
0000000000000000 u notmain_i

notmain.o:
0000000000000000 T _Z12notmain_funcv
0000000000000000 u notmain_i

man nm说说u

  

“ u”该符号是唯一的全局符号。这是对ELF符号绑定的标准集合的GNU扩展。对于这样的符号,动态链接器将确保在整个过程中                  只有一个使用此名称和类型的符号。

所以我们看到有一个专用的ELF扩展。