An appeal to the first part and additional questions about the loss of extern.
const std::string CONST_STR = "global string";
According to C ++ rules, this is identical:
static const std::string CONST_STR = "global string";
If it is in the included file, you will create different lines in each translation unit (TU). They all work fine on their own, but suppose you also added a function in one header:
inline void foo() { std::cout << CONST_STR; }
If the << operator accepts the string const& , then in each TU it will be bound to a separate line. Thus, the βone definition ruleβ is violated and placed in undefined (UB) behavior. In practice, this most likely works, but, nevertheless, it is UB.
The original extern form is similar to this, since the same-looking string literals are also separate in different TUs.
If you simply say extern without an initializer, this declaration will be resolved by the linker for a single definition. If you use an initializer, this makes it a definition. Thus, an object is created in each TU, but using a common public name, expecting other TUs to gain access to it. Implementation is exempt from liability as you must ensure that only one definition is actually provided.
Unfortunately, one rule of definition will break too easily, and most of its forms clearly allow the implementation to not produce any diagnostics. In practice, the linker simply selects a random definition from the pool. The double free one is probably caused by _atstart and _atexit for constructor and destructor calls, the object itself melts into one, then it receives as many constructor and destructor calls as you have TU.
For implementation, this is all fair play, since everything goes for UB.