C++常量的最佳实践
C++ Best practices for constants
我有一大堆常量,我想在代码的不同部分访问它们,但我想作为一个整体轻松访问它们:
static const bool doX = true;
static const bool doY = false;
static const int maxNumX = 5;
等等。
所以我创建了一个名为"constants.h"的文件,并将它们全部粘贴在其中,并#将其包含在任何需要知道常量的文件中。
问题是,这在编译时非常糟糕,因为每次我更改常量时,常量.h引用的所有文件都必须重新生成。(而且,据我所知,由于它们是静态的,每次在新的.cpp中包含常量.h时,我都会在代码中生成doX/doY/maxNumX的副本,这会导致编译的EXE中浪费数千字节的空间——有什么办法可以看到这一点吗?)。
所以,我想要一个解决方案。如果可能的话,它不是"只在使用常量的文件中声明常量"。
有什么建议吗?
唯一的选择是将常数设为extern
,并在另一个.cpp
文件中定义它们,但您将失去优化的潜力,因为编译器在编译每个.cpp`时不知道它们的值。
顺便说一句,不要担心大小的增加:对于积分类型,常量很可能直接内联在生成的机器代码中。
最后,static
不是必需的,因为默认情况下,const
全局变量是C++中的static
。
您在标头中将它们声明为extern
,并在实现文件中定义它们。
这样,当您想要更改它们的值时,您可以修改实现文件,而不需要完全重新编译。
变体中的问题与编译无关,而是与逻辑有关。它们不会是全局变量,因为每个翻译单元都有自己的变量副本。
编辑:
C++风格的方法实际上是将它们封装在一个类中:
//constants.h
class Constants
{
public:
static const bool doX;
static const bool doY;
static const int maxNumX;
}
//constants.cpp
const bool Constants::doX = true;
const bool Constants::doY = false;
const int Constants::maxNumX = 5;
我认为你的基本假设是错误的。
你的其他标题通常是通过把一起工作的内容放在一起来组织的。例如,一个类及其相关方法,或者两个类之间存在严重的相互关联。
为什么要将所有常量分组在一个标头中?这没有道理。简单地包含每一个依赖项,这和"global.h"
标头一样糟糕。
通常,常数是在特定的上下文中使用的。例如,用作特定函数标志的枚举:
class File {
public:
enum class Mode {
Read,
Write,
Append
};
File(std::string const& filename, Mode mode);
// ...
};
在这种情况下,这些常量与它们绑定到的类(甚至在类内)位于同一个头中,这是很自然的。
另一类常量是那些刚刚渗透到整个应用程序中的常量。例如:
enum class Direction {
Up,
Down,
Right,
Left,
Forward,
Backward
};
在游戏中,你想表达物体的移动方向。
在这种情况下,可以为这组特定的常量创建一个头文件。
如果你真的担心将这些文件分组在一起:
constants/
Direction.hpp
Sandwich.hpp
State.hpp
当您添加一个常量时,您将巧妙地避开重新编译整个应用程序的问题。。。不过,如果你需要的话,你只需支付一次费用,这比你在剩下的工作中不得不忍受的错误设计要好。
这种用法有什么问题
不要在头文件中声明static
类型,它不会做你认为它会做的事情。
当您在头文件中声明静态时,会在每个翻译单元(TU)中创建该变量的副本,其中包含该头文件,因此每个TU都会看到不同的变量,这与您期望的全局变量相反。
建议的解决方案:
您应该在头文件中将它们声明为extern
,并在一个cpp文件中定义它们,同时在要访问它们的每个cpp文件中将头与extern
一起包含。
阅读良好:
我应该如何使用extern
另一种最适合编译时(但运行时开销较小)的方法是通过类中的静态方法访问常量。
//constants.h
class Constants
{
public:
static bool doX();
static bool doY();
static int maxNumX();
};
//constants.cpp
bool Constants::doX() { return true; }
bool Constants::doY() { return false; }
int Constants::maxNumX() { return 42; }
这种方法的优点是,只有在头中添加/删除/更改方法的声明时,才能重新编译所有内容,而更改任何方法返回的值只需要编译constants.cpp(当然还有链接)。
与大多数事情一样,这可能是最好的,也可能不是最好的,这是你的特殊情况,但这是另一个需要考虑的选择。
直接的方法是创建非常量符号:
const bool doX = true;
const bool doY = false;
const int maxNumX = 5;
编译器将用给定的值替换这些值。这是最有效的方法。当然,这也会导致在修改或添加值后立即重新编译。但在大多数情况下,这不应该引起实际问题。
当然有不同的解决方案:
使用
static const
s(或静态常量类成员),可以在不重新编译所有引用文件的情况下修改值,但因此,这些值保存在常量数据段中,该数据段将在运行时调用,而不是在编译时解析。如果运行时性能没有问题(就像90%的典型代码一样),那没关系直接的C++方法是使用类
enums
,而不是全局常量标识符(正如我的Mathieu所指出的)。这更符合类型安全性,除此之外,它的工作原理与const
类似:符号将在编译时解析。
- #定义c-预处理器常量..我做错了什么
- 在C#中处理C++指针而不使用unsafe的最佳方法
- 用C++中的一个变量定义一个常量
- 什么时候在C++中返回常量引用是个好主意
- 代理对象的常量正确性
- 我想将一个对T类型的非常量左值引用绑定到一个T类型的临时值
- 在C++中,将大的无符号浮点数四舍五入为整数的最佳方法是什么
- 初始化不是整数的巨大常量多维数组的最佳方法是什么?
- 当通过常量和不是字符串的最佳选择时,是否有任何情况?
- 定义常量变量的最佳方法
- 在 c++ 中获取常量字符 * 长度的最佳方法
- 跨平台套接字发送,Linux 上的缓冲区常量无效* Windows上的常量字符*,最佳处理方式
- 定义uint64_t常量的最佳/正确方法
- 消除从字符串常量到'char*'的已弃用转换的最佳方法"
- C++常量访问器和参考最佳实践
- 常量最佳匹配函数与其他函数之间的歧义
- 使C++软件包和C#软件包访问相同枚举/常量int的最佳方法
- C++常量的最佳实践
- 在命名空间中定义双常量的最佳方式是什么
- C++初始化非常大的常量数组,最佳实践