1

我的 C 类中有一个静态 unordered_map。如果我将类定义和声明放在与包含函数 main 的文件不同的文件中,我会遇到行为差异。

问题是我观察到如果类 C 与函数 main 在同一个编译单元中,一切都很好,我只看到一次文本“创建新字符串:c”。但是,如果我将代码拆分为三个文件(参见下面的清单),我会看到两次“创建新字符串:c”,这意味着我的静态 unordered_map 在进入 main 之前就被擦除了

我的问题是:为什么会发生这种情况?(仅在使用 Apple LLVM 编译器 4.1 进行编译时才会出现差异。我已经使用 g++4.7 -std=c++11 对其进行了测试,并且拆分代码运行良好。)
提前感谢您的任何想法!

// would go to My_header.h

#include <unordered_map>
#include <string>
#include <iostream>

using namespace std;    

class C{
public:
  C(const string & s);
private:
  static unordered_map<string, string*> m;
  string *name;
};

// would go to My_code.cpp    
// (when separated, add #include "My_header.h")

unordered_map<string, string*> C::m;

C::C(const string & s):
name(NULL)
{
  string*& rs = m[s];
  if(rs)
  {
    name = rs;
  }
  else
  {
    cout<<"new string created: "<<s<<endl;
    rs = name = new string(s);
  }
}

// would go to main.cpp
// (when separated, add #include "My_header.h")

C c("c");

int main(int argc, const char * argv[])
{
  cout << "main" << endl;
  C c1("c");
}
4

2 回答 2

6

全局对象的初始化顺序仅在一个翻译单元内定义。在不同的翻译之间,不能保证顺序。std::unordered_map因此,您可能会看到在构造之前被访问所产生的行为。

避免这些问题的方法当然是不使用全局对象。如果您真的需要使用全局对象,最好用函数包装对象。这样可以保证对象在第一次被访问时被构造。使用 C++ 2011,构造甚至是线程安全的:

T& global() {
    static T rc;
    return rc;
}
于 2012-12-15T15:20:26.770 回答
0

多谢你们!按照 Dietmar 的建议,我这样做了:

class C{
//...
private:
  static unordered_map<string, string*>& m();
};

unordered_map<string, string*>& C::m()
{
  static unordered_map<string, string*> m;
  return m;
}

然后我一直提到m()。奇怪的是以前没有发生过。我想我很幸运。但是,这应该是一个警告信息的情况,不是吗?


为避免此类错误,我将使用以下宏来声明和定义静态变量:

/// Use this macro in classes to declare static variables
#define DECLARE_STATIC(type, name) static type& name();

/// Use this macro in definition files to define static variables
#define DEFINE_STATIC(type, name) type& name(){static type local; return local;}

这种情况下的用法:

class C{
//...
private:
  DECLARE_STATIC(unordered_map<string, string*>, m);
}
DEFINE_STATIC(unordered_map<string, string*>, m)
于 2012-12-15T15:31:05.847 回答