创建 class 的全局实例时,c++ 程序崩溃,其构造函数引用全局变量

c++ program crash when creating global instance of class whose constructor references a global variable

我正在尝试创建 class 的全局实例,其构造函数引用全局变量。

程序编译无误。但是当它是运行时,它会在全局变量的引用上崩溃。

如何在构造函数不崩溃的情况下创建此 class 的全局实例?

这是我做的SSCCE:

/* main.cpp */
#include "TestClass.h"

// I need a global instance of TestClass
TestClass j;

int main()
{
    return 0;
}

-

/* C.h */
#ifndef C_H_INCLUDED
#define C_H_INCLUDED

#include <string>

// global
extern const std::string S;

#endif // C_H_INCLUDED

-

/* C.cpp */
#include "C.h"

#include <string>

// extern definition of global
const std::string S = "global string data";

-

/* TestClass.h */
#ifndef TESTCLASS_H_INCLUDED
#define TESTCLASS_H_INCLUDED

class TestClass
{
public:
    TestClass();
};

#endif // TESTCLASS_H_INCLUDED

-

/* TestClass.cpp */
#include "TestClass.h"

#include <iostream>

#include "C.h"  // for S global

TestClass::TestClass()
{
    std::cout << S << std::endl;  // this line crashes the program
}

有关崩溃的调试器消息:

Program received signal SIGSEGV, Segmentation fault.
In std::basic_ostream<char, std::char_traits<char> >& std::operator<< <char, std::char_traits<char>, std::allocator<char> >(std::basic_ostream<char, std::char_traits<char> >&, std::basic_string<char, std::char_traits<char>, std::allocator<char> > const&) () ()
#1  0x004014f9 in TestClass::TestClass (this=0x4a0024 <j>) at E:\cpp\externconsttest\TestClass.cpp:9
E:\cpp\externconsttest\TestClass.cpp:9:117:beg:0x4014f9
At E:\cpp\externconsttest\TestClass.cpp:9
#1  0x004014f9 in TestClass::TestClass (this=0x4a0024 <j>) at E:\cpp\externconsttest\TestClass.cpp:9
E:\cpp\externconsttest\TestClass.cpp:9:117:beg:0x4014f9

此示例在 operator<< 中崩溃,但无论以何种方式引用 S,它都会在任何对 S 的引用上崩溃。

C++ 不提供语法来控制不同编译单元中全局作用域对象的构造函数序列。此外,下一个版本可能会更改顺序。

我们使用的机制:

  • 创建初始化为空指针的全局范围指针。

  • 在 main 启动之后但在任何线程启动之前,以合理的顺序新建对象。

所以,在这种情况下...

TestClass* j = nullptr; 

int main(...)
{
   // .. other init
   /* const std::string* */ 
   S = new std::string ("global string data");

   // now that S exists,   it is ok to intantiate:
   /* TestClass() */
   j = new TestClass;

   // much more stuff

   return(0);
}

显式控制总是可以工作的。

另一个好处 - 一致的启动时间。

一个相关的陷阱 - 大多数模式书模式都不是线程安全的。在无法控制全局范围 ctors 的情况下组合多个线程可能非常难以调试。

我猜它崩溃是因为在调用 TestClass 的构造函数时全局 const std::string S 尚未初始化 。这是 C++ 中全局变量和静态变量的普遍问题:通常您不知道全局变量和静态变量的初始化顺序(实际上它们是按照您在链接阶段将目标文件传递给链接器的顺序初始化的——但这是不是很有帮助)。这个问题有几种不同的解决方案。其中之一是:

  1. 创建一个在函数体中包含静态变量的函数,returns 引用该变量(而不是仅仅使用全局变量,您可以调用该函数)。这类似于 单例 设计模式:

    const std::string& get_my_string() { static const std::string S; return S; }

然后在你的构造函数中:

TestClass::TestClass()
{
    std::cout << get_my_string() << std::endl;
}

调用get_my_string将强制初始化静态字符串仅一次(第一次调用该函数)恰好在你需要它。请注意,此示例未考虑线程(在多线程应用程序中,您应该同步 get_my_string() 函数以保护静态字符串的初始化)。

希望对您有所帮助。

顺便说一下:您的全局 TestClass j.

可能会遇到类似的问题

这样你只会解决一半的问题 - 初始化(你仍然不知道销毁的顺序) - 在大多数情况下就足够了。

另一种选择是在堆上创建字符串(可能使用如上所述的类似方法)- 您只需要 delete 在您知道这样做是安全的时候就可以了。