我想在C++中定义一个常量,以便在几个源文件中可见.我可以想象以下方法在头文件中定义它:
#define GLOBAL_CONST_VAR 0xFF
int GLOBAL_CONST_VAR = 0xFF;
int get_GLOBAL_CONST_VAR()
)enum { GLOBAL_CONST_VAR = 0xFF; }
const int GLOBAL_CONST_VAR = 0xFF;
extern const int GLOBAL_CONST_VAR;
并在一个源文件中 const int GLOBAL_CONST_VAR = 0xFF;
选项(1) - 绝对不是您想要使用的选项
选项(2) - 使用头文件在每个目标文件中定义变量的实例
选项(3) - 在大多数情况下,IMO过度杀戮
选项(4) - 在许多情况下可能不好,因为枚举没有具体类型(C++ 0X将增加定义类型的可能性)
所以在大多数情况下我需要在(5)和(6)之间进行选择.我的问题:
Nik*_*sov 65
绝对使用选项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;
Run Code Online (Sandbox Code Playgroud)
Bli*_*ndy 30
(5)准确地说出你想说的话.此外,它允许编译器在大多数情况下优化它.(6)另一方面不会让编译器优化它,因为编译器不知道你最终是否会改变它.
AnT*_*AnT 23
(5)比(6)"更好",因为它GLOBAL_CONST_VAR
在所有翻译单元中定义为积分常数表达式(ICE).例如,您可以在所有翻译单元中将其用作数组大小和案例标签.在(6)的情况下,GLOBAL_CONST_VAR
仅在该转换单元中定义ICE并且仅在定义点之后.在其他翻译单元中,它不能用作ICE.
但是,请记住(5)给出GLOBAL_CONST_VAR
内部链接,这意味着GLOBAL_CONST_VAR
每个翻译单元中的"地址标识" 将不同,即在每个翻译单元&GLOBAL_CONST_VAR
中将给出不同的指针值.在大多数用例中,这并不重要,但如果你需要一个具有一致全局"地址标识"的常量对象,那么你必须使用(6),牺牲常量的ICE-ness.处理.
此外,当常量的ICE不是问题(不是整数类型)并且类型的大小变大(不是标量类型)时,则(6)通常变得比(5)更好的方法.
(2)不正常,因为GLOBAL_CONST_VAR
in(2)默认有外部链接.如果你把它放在头文件中,你通常会得到多个定义GLOBAL_CONST_VAR
,这是一个错误.const
默认情况下,C++中的对象具有内部链接,这就是为什么(5)工作(这就是为什么,正如我上面所说,你GLOBAL_CONST_VAR
在每个翻译单元中得到一个独立的,独立的).
Cir*_*四事件 10
C++17inline
变量
这个很棒的 C++17 功能使我们能够:
constexpr
:如何声明 constexpr extern?主程序
#include <cassert>
#include "notmain.hpp"
int main() {
// Both files see the same memory address.
assert(¬main_i == notmain_func());
assert(notmain_i == 42);
}
Run Code Online (Sandbox Code Playgroud)
notmain.hpp
#ifndef NOTMAIN_HPP
#define NOTMAIN_HPP
inline constexpr int notmain_i = 42;
const int* notmain_func();
#endif
Run Code Online (Sandbox Code Playgroud)
notmain.cpp
#include "notmain.hpp"
const int* notmain_func() {
return ¬main_i;
}
Run Code Online (Sandbox Code Playgroud)
编译并运行:
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
Run Code Online (Sandbox Code Playgroud)
另请参阅:内联变量如何工作?
内联变量的 C++ 标准
C++ 标准保证地址相同。C++17 N4659 标准草案 10.1.6“内联说明符”:
6 具有外部链接的内联函数或变量在所有翻译单元中应具有相同的地址。
cppreference https://en.cppreference.com/w/cpp/language/inline解释说,如果static
未给出,则它具有外部链接。
内联变量的实现
我们可以观察它是如何实现的:
nm main.o notmain.o
Run Code Online (Sandbox Code Playgroud)
其中包含:
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
Run Code Online (Sandbox Code Playgroud)
并man nm
说u
:
“u”符号是全球独一无二的符号。这是 ELF 符号绑定标准集的 GNU 扩展。对于这样的符号,动态链接器将确保在整个过程中只有一个具有该名称和类型的符号正在使用。
所以我们看到有一个专门的 ELF 扩展用于此。
在 GCC 7.4.0、Ubuntu 18.04 上测试。
小智 6
如果您使用C ++ 11或更高版本,请尝试使用编译时常量:
constexpr int GLOBAL_CONST_VAR{ 0xff };
Run Code Online (Sandbox Code Playgroud)
如果它将是一个常数,那么你应该将它标记为常数 - 这就是为什么2在我看来是不好的.
编译器可以使用值的const特性来扩展一些数学,以及使用该值的其他操作.
选择5到6 - hmm; 5对我来说感觉更好.
在6)中,该值与其声明不必要地脱离.
我通常会有一个或多个这些标题只定义其中的常量等,然后没有其他"聪明"的东西 - 很好的轻量级标题,可以很容易地包含在任何地方.
回答你的第二个问题:
(2) 是非法的,因为它违反了单一定义规则。它GLOBAL_CONST_VAR
在包含它的每个文件中定义,即不止一次。(5) 是合法的,因为它不受单一定义规则的约束。每个GLOBAL_CONST_VAR
都是一个单独的定义,在包含它的那个文件中是本地的。当然,所有这些定义共享相同的名称和值,但它们的地址可能不同。
归档时间: |
|
查看次数: |
86013 次 |
最近记录: |