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

时间:2015-06-15 01:38:47

标签: c++ global-variables global

我试图创建一个构造函数引用全局变量的类的全局实例。

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

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

这是我做的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

此示例在运算符&lt;&lt;中崩溃,但无论引用的方式如何,它都会在对S的任何引用中崩溃。

2 个答案:

答案 0 :(得分:5)

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

  1. 在其主体中创建一个带有静态变量的函数,该函数返回对变量的引用(而不是仅使用您将调用该函数的全局变量)。这与 singleton 设计模式类似:

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

  2. 然后在你的构造函数中:

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

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

    我希望有所帮助。

    顺便说一句:您可能会遇到全局TestClass j的相同问题。

    这样你只能解决问题的一半 - 初始化(你仍然不知道破坏的顺序) - 在大多数情况下它是足够的。

    另一种选择是在堆上创建字符串(可能使用上面描述的simmilar方法) - 当你知道这样做时,你只需要delete

答案 1 :(得分:1)

C ++不提供控制不同编译单元中全局范围对象的ctor序列的语义。此外,下一个版本可能会改变序列。

我们使用的机制:

  • 创建全局范围指针,初始化为null ptr。

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

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

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很难调试。