我正在开发的一个程序中有很多常量,它们适用于所有类。我想创建一个名为 "Constants.h" 的头文件,并能够声明所有相关的常量。然后在我的其他类中,我只需要包含#include"Constants.h"
。
我使用#ifndef
... #define ...
语法使其正常工作。但是,我更喜欢使用const int...
形式的常量。不过我不太确定如何做到这一点。
const ints
:// Constants.h
#if !defined(MYLIB_CONSTANTS_H)
#define MYLIB_CONSTANTS_H 1
const int a = 100;
const int b = 0x7f;
#endif
static const int a = 100;
static const int b = 0x7f;
enum mylib_constants {
a = 100;
b = 0x7f;
};
extern const int
和单独的实现文件可以防止名称被用作编译时常量。
int * const ptr;
为了使规则适用于常量指针,需要将其设置为常量指针。
还要注意的是,这是我喜欢在类型后一致地放置 const
的原因之一:int const
而不是 const int
。我也将 *
放在变量旁边:即 int *ptr;
而不是 int* ptr;
(也可以参考 this 讨论)。
我喜欢做这些事情,因为它们反映了 C++ 实际工作的一般情况。备选项(const int
,int* p
)只是为了使某些简单的东西更易读而特殊处理。问题是,当您走出这些简单的情况时,特殊处理的备选方案会变得具有误导性。
因此,尽管前面的示例展示了 const
的常见用法,但我实际上建议人们像这样编写它们:
int const a = 100;
int const b = 0x7f;
并且
static int const a = 100;
static int const b = 0x7f;
const
放在正确的位置上。将指针变量设为 const
需要在 *
后面添加 const
。例如:int * const x
。如果你只是使用 const int *x;
,那么 const
关键字会应用于指针所指向的内容而不是指针本身。 - bames53foo
时,在此文件中包含了该头文件,但编译器无法知道编译时应该使用哪个值,因为编译器在编译使用foo
的源文件时并不知道设置foo
值的文件是哪一个。这是由于C++的"分离编译"模型导致的。甚至可以有不同的实现文件以不同的方式设置foo
,并根据链接的实现文件的不同来更改程序。 - bames53我认为在这种情况下,命名空间更适合。
选项1:
#ifndef MYLIB_CONSTANTS_H
#define MYLIB_CONSTANTS_H
// File Name : LibConstants.hpp Purpose : Global Constants for Lib Utils
namespace LibConstants
{
const int CurlTimeOut = 0xFF; // Just some example
...
}
#endif
// source.cpp
#include <LibConstants.hpp>
int value = LibConstants::CurlTimeOut;
选项二:
#ifndef MYLIB_CONSTANTS_H
#define MYLIB_CONSTANTS_H
// File Name : LibConstants.hpp Purpose : Global Constants for Lib Utils
namespace CurlConstants
{
const int CurlTimeOut = 0xFF; // Just some example
...
}
namespace MySQLConstants
{
const int DBPoolSize = 0xFF; // Just some example
...
}
#endif
// source.cpp
#include <LibConstants.hpp>
int value = CurlConstants::CurlTimeOut;
int val2 = MySQLConstants::DBPoolSize;
value
、val2
和其他所有常量?有没有一种方法可以在函数内部包含命名空间并使用与命名空间中相同的常量名称? - talekeDskobeDachar *
中使用 const char * const
。 - basilC++17 inline
变量
这个很棒的C++17特性允许我们:
constexpr
: 如何声明constexpr extern?main.cpp
#include <cassert>
#include "notmain.hpp"
int main() {
// Both files see the same memory address.
assert(¬main_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 ¬main_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
另请参阅:如何使用内联变量?
C++标准中的内联变量
C++标准保证地址相同。 C++17 N4659标准草案 10.1.6 "The inline specifier":
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
命令中的符号,它是一个独特的全局符号。这是标准ELF符号绑定的GNU扩展。对于这样的符号,动态链接器将确保在整个进程中只使用一种此名称和类型的符号。
因此,我们可以看到有一个专用的ELF扩展来实现这个功能。
C++17标准草案关于“全局”const
意味着static
以下是https://dev59.com/bmct5IYBdhLWcg3wgNnR#12043198提到的引用:
C++17 n4659标准草案 6.5 "程序和链接":
3.如果一个具有命名空间作用域(6.3.6)的名称是:
- (3.1) —— 显式声明为静态变量、函数或函数模板;或
- (3.2) —— 非内联非易失性const限定类型的非内部链接变量,既不显式声明为extern也未先前声明为外部链接;或
- (3.3) —— 匿名联合体的数据成员。
则该名称具有内部链接。
“命名空间”作用域通常是我们口头上所说的“全局”。
附录C(信息性的)兼容性,C.1.2
第6条:“基本概念”给出了为什么从C中更改这个的理由:
6.5 [也是10.1.7]
更改:在C++中,如果一个文件作用域的const变量没有显式声明为extern,则它具有内部链接,而在C中它将具有外部链接。
原因:因为在C++中,const对象可以在翻译期间用作值,所以这个特性促使程序员为每个const对象提供显式初始化程序。此功能允许用户将const对象放在多个翻译单元中包含的源文件中。
对原始特性的影响:更改到良好定义的特性的语义。
转换的难度:语义转换。
使用范围:很少。
另请参见:为什么在C++中const意味着内部链接,而在C中不是?
在Ubuntu 18.04的GCC 7.4.0中进行了测试。
const int
的语句。这是因为变量会在每个源文件(严格来说是翻译单元)中定义一次,由于全局const
变量隐含为静态变量,会占用更多的内存。Constants.cpp
来实际定义变量,然后在头文件中将变量声明为extern
。// Protect against multiple inclusions in the same source file
#ifndef CONSTANTS_H
#define CONSTANTS_H
extern const int CONSTANT_1;
#endif
而这是一个源文件中的内容:
const int CONSTANT_1 = 123;
const int
,因为默认情况下const
变量具有内部链接。是否应该这样做而不是使用单个实例是另一个问题;通常只要值在头文件中可用,就没有太大区别。 - Mike Seymourextern
声明的头文件。 - Some programmer dude不要创建大量的全局变量,你可以考虑创建一个类,其中包含许多公共静态常量。这仍然是全局的,但是这样它被包装在一个类中,所以你知道常量来自哪里,并且它应该是一个常量。
Constants.h
#ifndef CONSTANTS_H
#define CONSTANTS_H
class GlobalConstants {
public:
static const int myConstant;
static const int myOtherConstant;
};
#endif
Constants.cpp
#include "Constants.h"
const int GlobalConstants::myConstant = 1;
const int GlobalConstants::myOtherConstant = 3;
#include "Constants.h"
void foo() {
int foo = GlobalConstants::myConstant;
}
看起来bames53的答案可以扩展到在命名空间和类声明中定义整数和非整数常量值,即使它们被包含在多个源文件中也是如此。不必将声明放在头文件中,但需要将定义放在源文件中。以下示例适用于Microsoft Visual Studio 2015,z/OS V2.2 XL C/C++ on OS/390以及GNU/Linux 4.16.14(Fedora 28)上的g++(GCC)8.1.1 20180502。请注意,常量仅在单个头文件中声明/定义,该头文件被包含在多个源文件中。
在foo.cc中:
#include <cstdio> // for puts
#include "messages.hh"
#include "bar.hh"
#include "zoo.hh"
int main(int argc, const char* argv[])
{
puts("Hello!");
bar();
zoo();
puts(Message::third);
return 0;
}
在messages.hh文件中:
#ifndef MESSAGES_HH
#define MESSAGES_HH
namespace Message {
char const * const first = "Yes, this is the first message!";
char const * const second = "This is the second message.";
char const * const third = "Message #3.";
};
#endif
#include "messages.hh"
#include <cstdio>
void bar(void)
{
puts("Wow!");
printf("bar: %s\n", Message::first);
}
#include <cstdio>
#include "messages.hh"
void zoo(void)
{
printf("zoo: %s\n", Message::second);
}
In bar.hh:
#ifndef BAR_HH
#define BAR_HH
#include "messages.hh"
void bar(void);
#endif
#ifndef ZOO_HH
#define ZOO_HH
#include "messages.hh"
void zoo(void);
#endif
这将产生以下输出:
Hello!
Wow!
bar: Yes, this is the first message!
zoo: This is the second message.
Message #3.
char const * const
表示指向常量字符数组的常量指针。第一个const
是必需的,因为(根据g++)“ISO C ++禁止将字符串常量转换为'char *'”。第二个const
是必需的,以避免由于多个定义而导致链接错误(然后不够常量)。如果省略其中一个或两个const
,则您的编译器可能不会抱怨,但源代码的可移植性较差。
const int
不理解的地方吗?如果这是你的问题,可以贴出编译失败的示例代码吗? - djechlin