我对在整个程序中一次性使用全局变量很感兴趣。所以我认为实现这一点的最好方法是在头文件中定义它,如下所示:
extern const std::string CONST_STR = "global string";
但这导致了"双重免费或腐败"的运行时错误。删除extern
使问题消失。
- 有人能解释这种行为吗
- AFAIK,如果没有
extern
定义,每个翻译单元都会有一个CONstrongTR,难道没有办法获得一个完全常量的全局变量吗
解决第一部分和关于丢失extern的其他问题。
const std::string CONST_STR = "global string";
根据C++规则,这与说:相同
static const std::string CONST_STR = "global string";
如果这是在包含文件中,您将在每个翻译单元(TU)中创建不同的字符串。它们自己都能很好地工作,但假设你也在同一个标题中添加了一个函数:
inline void foo() { std::cout << CONST_STR; }
如果<<
运算符通过const&
获取字符串,则在每个TU中,它将绑定到一个单独的字符串。因此违反了"一个定义规则",并将您置于未定义的行为(UB)。在实践中,它很可能是有效的,但它仍然是UB。
最初的extern
形式与此类似,因为看起来相同的字符串文字在不同的TU中也是分开的。
如果只说extern
而没有初始化器,那么它就是一个声明,并且将由链接器解析为单个定义。如果你使用一个初始值设定项,那么它就是一个定义。因此,再次在每个TU中创建对象,但使用一个公共名称,期望其他TU访问它。由于必须确保实际只提供一个定义,因此实现免除了责任。
不幸的是,一个定义规则太容易被打破,而且它的大多数形式都明确允许实现不发布任何诊断。实际上,链接器只是从池中随机选择一个定义。双重释放可能是由于为构造函数和析构函数调用发出_atstart
和_atexit
条目引起的,对象本身被融化为一个,然后获得与TU一样多的构造函数和析构函数调用。
对于实现来说,这都是公平的游戏,对于UB来说,任何事情都会发生。
放置定义
const std::string CONST_STR = "global string";
在一个编译单元中(传统的做法是将其放在源文件中)。
在标题中,只写声明:
extern const std::string CONST_STR;
这将确保在整个程序中只有一个版本的字符串。我很惊讶你的链接器没有抱怨你的方式。