C++定义跨文件常量的最佳方法

11

我正在制作一个游戏,并有一个有趣的问题。我有一些希望在一个文件中实现的游戏范围常量值。目前我有类似这样的代码:

constants.cpp

extern const int BEGINNING_HEALTH = 10;
extern const int BEGINNING_MANA = 5;

constants.hpp

extern const int BEGINNING_HEALTH;
extern const int BEGINNING_MANA;

然后文件只需#include "constants.hpp"。

这很好用,直到我需要使用一个常量作为模板参数,因为外部链接的常量不是有效的模板参数。所以我的问题是,实现这些常量的最佳方法是什么?我担心简单地将常量放在头文件中将导致它们在每个翻译单位中被定义。而且我不想使用宏。

谢谢

8个回答

19

去掉extern即可解决问题。

这段代码在头文件中可以完美运行,因为所有内容都是“真正的常量”,因此具有内部链接:

const int BEGINNING_HEALTH = 10;
const int BEGINNING_MANA = 5;
const char BEGINNING_NAME[] = "Fred";
const char *const BEGINNING_NAME2 = "Barney";

这段代码不能安全地放在头文件中,因为每一行都具有外部链接(无论是显式还是因为不是真正的常量):

extern const int BEGINNING_HEALTH = 10;
extern const int BEGINNING_MANA = 5;
const char *BEGINNING_NAME = "Wilma";  // the characters are const, but the pointer isn't

我怀疑最后一个 BEGINNING_NAME[] 的代码存在复制/粘贴错误。你是想把它写成 BEGINNING_NAME 吗? - Johannes Schaub - litb
你需要使用“static”,否则你将无法获得内部链接或者更理想的情况是等同于一个带有内联值的“#define”。 - Jim Buck
@Jim Buck 在C++中,常量具有内部链接 - 不需要使用“static”。 - anon

10

枚举类型怎么样?

constants.hpp

  enum {
    BEGINNING_HEALTH = 10,
    BEGINNING_MANA = 5
  }

7

在您的.hpp文件中使用"static const int",并且在.cpp文件中不要放置任何内容(当然除了其他代码)。


4

使用命名空间:

namespace GameBeginning {
    const int HEALTH = 10;
    const int MANA   = 5; 
};

接下来,您可以使用 player.health = GameBeginning::HEALTH; 这行代码。


0

曾经的简单去哪里了:

#define BEGINNING_HEALTH 10

哇,那些日子真是美好啊。
哦等等,那些日子现在依然美好!


在模板参数的上下文中,那可能不会做你期望的事情... :) rlbond 需要那个。 - Mihai Limbășan
5
有些人希望我们的调试器显示“BEGINNING_HEALTH”而不是“10”。 - JoeG
“当你有 printf() 时,谁还需要调试器呢?” - slacy
2
printf,#define,接下来做什么。这是C++。 - Sebastian Mach

0

大多数编译器根本不为const POD值分配空间。它们会将其优化并将其视为已被#define,是吗?


接近了,但还不完全一样。宏起作用而常量不起作用的示例:#define FOO "foo"; \ const char *str = "bar" FOO; C和C++允许将字符串字面标记连接在一起,但不允许连接字符串常量。 - Tom
这仅适用于整型常量。如果禁用优化,浮点数、双精度浮点数、字符指针和其他类型将被分配存储空间。 - Adam Rosenfield
@Tom:那不是我想表达的意思。我的意思是针对POD常量,而不是预处理器魔法。我的意思是指除非你尝试获取其地址,否则该符号和值的空间不存在。 - greyfade
我认为POD类型官方上包括浮点数和双精度数,但是这种优化方式通常不会应用于浮点数和双精度数。 - Max Lybbert

-2

也许可以考虑使用静态类?

class CONSTANTS {
public:
static inline int getMana() { return 10;};
};

那并没有帮助。函数值不能用作模板参数。C++0x 关键字 constexpr 旨在解决这个问题。此外,C++ 还有命名空间,它们比静态类更适用于“名称空间”常量。 - Tom

-5
作为对标题问题的快速回答,单例模式是一种可能的最佳C++方式来定义跨文件常量并确保对象只有一个实例。
至于模板参数问题,您需要传递类型而不是值。您的类型是“int”。

1
这似乎对于一组简单的常量来说有些过度,也不太可能解决他在模板实例化方面遇到的问题。 - Eclipse
为什么他不能通过模板实例化来解决他的问题,因为它不会是一个外部变量,而是一个局部变量。过度设计是相对的,从质量角度来看,是100M、100K还是100个游戏预算? - jeffD
jeffD,问题在于你的回答没有意义。你想把一个“int”作为单例吗?int 是由值组成的,而不是由身份组成的。另一个可能让人们对你进行投票的原因是他不想传递类型,而是要将值传递给他的模板。你为什么说他不能这样做? - Johannes Schaub - litb
这个问题的标题本身就是一个好问题。也许我确实需要看到他的更多代码。请参考http://www.cplusplus.com/doc/tutorial/templates.html获取更多信息。“模板参数是一种特殊的参数,可用于将类型作为参数传递”。希望评论允许超链接。 - jeffD
很遗憾,JeffD,cplusplus.com上的教程并不是很好——有些地方不够完整,有时候也会出错。例如,模板声明并不像他们所说的那样是“表达式”,而是声明。他们在页面上也模糊了特化和显式特化之间的区别。 - Johannes Schaub - litb
显示剩余5条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接