4

我有兴趣在整个程序中一次性使用一个全局变量。所以我认为实现这一点的最佳方法是在头文件中定义它,如下所示:

extern const std::string CONST_STR = "global string";

但这导致了“双重释放或损坏”运行时错误。放弃extern使问题消失。

  1. 谁能解释这种行为?
  2. AFAIK,如果没有extern定义,每个翻译单元都会有一个 CONST_STR,难道没有办法获得一个完全 const 全局变量吗?
4

2 回答 2

4

解决第一部分和关于失去 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 什么都可以。

于 2013-06-27T15:07:34.723 回答
3

放置定义

const std::string CONST_STR = "global string";

在一个编译单元中(通常的做法是将它放在源文件中)。

在标题中,只写声明

extern const std::string CONST_STR;

这将确保您在整个程序中只有一个版本的字符串。鉴于您的方式,我很惊讶您的链接器没有抱怨。

于 2013-06-27T13:51:51.713 回答