c++:全局外部变量的“双重释放或损坏”?

Sub*_*way 5 c++ constants global-variables extern

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

extern const std::string CONST_STR = "global string";
Run Code Online (Sandbox Code Playgroud)

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

  1. 有人能解释这种行为吗?
  2. AFAIK,如果没有extern定义,每个翻译单元都会有一个 CONST_STR,是否有一种方法可以获得一个完全 const 全局变量?

Bal*_*Pal 5

解决第一部分和有关失去外部人员的其他问题。

const std::string CONST_STR = "global string";
Run Code Online (Sandbox Code Playgroud)

根据 C++ 规则,这等同于:

static const std::string CONST_STR = "global string";
Run Code Online (Sandbox Code Playgroud)

如果它位于包含文件中,您将在每个翻译单元 (TU) 中创建不同的字符串。它们本身都可以正常工作,但假设您还在同一标头中添加了一个函数:

inline void foo() { std::cout << CONST_STR; }
Run Code Online (Sandbox Code Playgroud)

如果<<运算符通过 获取字符串const&,则在每个 TU 中它将绑定到一个单独的字符串。因此违反了“单一定义规则”并使您陷入未定义行为(UB)。在实践中它很可能有效,但它仍然是 UB。

原始extern形式与此类似,因为相同的字符串文字在不同的 TU 中也是分开的。

如果您只是说extern没有初始化程序,那么它就是一个声明,并将由链接器解析为单个定义。如果您使用初始值设定项,则它会成为一个定义。因此,该对象再次在每个 TU 中创建,但使用公共公共名称,期望其他 TU 访问它。由于您必须确保实际上只提供一个定义,因此实现免除了责任。

不幸的是,单一定义规则太容易被打破,并且它的大多数形式明确允许实现不发出任何诊断。实际上,链接器只是从池中选择一个随机定义。双重释放可能是由构造函数和析构函数调用的发出_atstart_atexit条目引起的,对象本身被融化为一个对象,然后获得与 TU 一样多的构造函数和析构函数调用。

对于实现来说,这都是公平的游戏,至于 UB 则一切皆有可能。