У меня есть статическое unordered_map в моем классе C. Я испытываю различие в поведении, если я помещаю свое определение класса и объявление в разные файлы из файла, содержащего функцию main.
Дело в том, что я заметил, что если класс C находится в той же единице компиляции, что и функция main, все хорошо, я вижу только один раз текст «новая строка создана: c». Однако, если я разделю свой код на три файла (см. Листинг ниже), я увижу «новая строка создана: c» дважды, что означает, что мой статический unordered_map стирается прямо перед входом в основной.
Мой вопрос будет: почему это происходит? (Разница возникает только при компиляции с помощью компилятора 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");
}
Порядок инициализации глобальных объектов определяется только в пределах одной единицы перевода. Между разными переводами порядок не гарантируется. Таким образом, вы, вероятно, видите поведение в результате std::unordered_map
доступ к нему до его создания.
Конечно, чтобы избежать этих проблем, не используйте глобальные объекты. Если вам действительно нужно использовать глобальный объект, лучше всего обернуть объект функцией. Таким образом, гарантируется, что объект создается при первом обращении к нему. С C ++ 2011 конструкция даже поточно-ориентированная:
T& global() {
static T rc;
return rc;
}
Спасибо, парни! Следуя совету Дитмара, я сделал это:
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)