我有兴趣在整个程序中一次性使用一个全局变量。所以我认为实现这一点的最佳方法是在头文件中定义它,如下所示:
extern const std::string CONST_STR = "global string";
但这导致了“双重释放或损坏”运行时错误。放弃extern
使问题消失。
- 谁能解释这种行为?
- AFAIK,如果没有
extern
定义,每个翻译单元都会有一个 CONST_STR,难道没有办法获得一个完全 const 全局变量吗?
我有兴趣在整个程序中一次性使用一个全局变量。所以我认为实现这一点的最佳方法是在头文件中定义它,如下所示:
extern const std::string CONST_STR = "global string";
但这导致了“双重释放或损坏”运行时错误。放弃extern
使问题消失。
extern
定义,每个翻译单元都会有一个 CONST_STR,难道没有办法获得一个完全 const 全局变量吗? 解决第一部分和关于失去 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;
这将确保您在整个程序中只有一个版本的字符串。鉴于您的方式,我很惊讶您的链接器没有抱怨。