这篇文章可能看起来太长了,只是最后的简短问题。但我还需要描述一个我刚刚提出的设计模式。也许它很常用,但我从未见过它(或者它只是不起作用:)。
首先,这里的代码(据我所知)由于“静态初始化顺序惨败”而具有未定义的行为。问题是 Spanish::s_englishToSpanish 的初始化依赖于 English::s_numberToStr,它们都是静态初始化的并且在不同的文件中,所以这些初始化的顺序是未定义的:
文件:English.h
#pragma once
#include <vector>
#include <string>
using namespace std;
struct English {
static vector<string>* s_numberToStr;
string m_str;
explicit English(int number)
{
m_str = (*s_numberToStr)[number];
}
};
文件:英文.cpp
#include "English.h"
vector<string>* English::s_numberToStr = new vector<string>( /*split*/
[]() -> vector<string>
{
vector<string> numberToStr;
numberToStr.push_back("zero");
numberToStr.push_back("one");
numberToStr.push_back("two");
return numberToStr;
}());
文件:西班牙语.h
#pragma once
#include <map>
#include <string>
#include "English.h"
using namespace std;
typedef map<string, string> MapType;
struct Spanish {
static MapType* s_englishToSpanish;
string m_str;
explicit Spanish(const English& english)
{
m_str = (*s_englishToSpanish)[english.m_str];
}
};
文件:西班牙语.cpp
#include "Spanish.h"
MapType* Spanish::s_englishToSpanish = new MapType( /*split*/
[]() -> MapType
{
MapType englishToSpanish;
englishToSpanish[ English(0).m_str ] = "cero";
englishToSpanish[ English(1).m_str ] = "uno";
englishToSpanish[ English(2).m_str ] = "dos";
return englishToSpanish;
}());
文件:StaticFiasco.h
#include <stdio.h>
#include <tchar.h>
#include <conio.h>
#include "Spanish.h"
int _tmain(int argc, _TCHAR* argv[])
{
_cprintf( Spanish(English(1)).m_str.c_str() ); // may print "uno" or crash
_getch();
return 0;
}
为了解决静态初始化顺序问题,我们使用construct-on-first-use 习惯用法,并使这些静态初始化函数本地化,如下所示:
文件:English.h
#pragma once
#include <vector>
#include <string>
using namespace std;
struct English {
string m_str;
explicit English(int number)
{
static vector<string>* numberToStr = new vector<string>( /*split*/
[]() -> vector<string>
{
vector<string> numberToStr_;
numberToStr_.push_back("zero");
numberToStr_.push_back("one");
numberToStr_.push_back("two");
return numberToStr_;
}());
m_str = (*numberToStr)[number];
}
};
文件:西班牙语.h
#pragma once
#include <map>
#include <string>
#include "English.h"
using namespace std;
struct Spanish {
string m_str;
explicit Spanish(const English& english)
{
typedef map<string, string> MapT;
static MapT* englishToSpanish = new MapT( /*split*/
[]() -> MapT
{
MapT englishToSpanish_;
englishToSpanish_[ English(0).m_str ] = "cero";
englishToSpanish_[ English(1).m_str ] = "uno";
englishToSpanish_[ English(2).m_str ] = "dos";
return englishToSpanish_;
}());
m_str = (*englishToSpanish)[english.m_str];
}
};
但现在我们还有另一个问题。由于函数局部静态数据,这些类都不是线程安全的。为了解决这个问题,我们为这两个类添加了一个静态成员变量和一个初始化函数。然后在这个函数内部,我们通过调用每个具有函数局部静态数据的函数来强制初始化所有函数局部静态数据。因此,实际上我们在程序开始时初始化了所有内容,但仍控制初始化的顺序。所以现在我们的类应该是线程安全的:
文件:English.h
#pragma once
#include <vector>
#include <string>
using namespace std;
struct English {
static bool s_areStaticsInitialized;
string m_str;
explicit English(int number)
{
static vector<string>* numberToStr = new vector<string>( /*split*/
[]() -> vector<string>
{
vector<string> numberToStr_;
numberToStr_.push_back("zero");
numberToStr_.push_back("one");
numberToStr_.push_back("two");
return numberToStr_;
}());
m_str = (*numberToStr)[number];
}
static bool initializeStatics()
{
// Call every member function that has local static data in it:
English english(0); // Could the compiler ignore this line?
return true;
}
};
bool English::s_areStaticsInitialized = initializeStatics();
文件:西班牙语.h
#pragma once
#include <map>
#include <string>
#include "English.h"
using namespace std;
struct Spanish {
static bool s_areStaticsInitialized;
string m_str;
explicit Spanish(const English& english)
{
typedef map<string, string> MapT;
static MapT* englishToSpanish = new MapT( /*split*/
[]() -> MapT
{
MapT englishToSpanish_;
englishToSpanish_[ English(0).m_str ] = "cero";
englishToSpanish_[ English(1).m_str ] = "uno";
englishToSpanish_[ English(2).m_str ] = "dos";
return englishToSpanish_;
}());
m_str = (*englishToSpanish)[english.m_str];
}
static bool initializeStatics()
{
// Call every member function that has local static data in it:
Spanish spanish( English(0) ); // Could the compiler ignore this line?
return true;
}
};
bool Spanish::s_areStaticsInitialized = initializeStatics();
问题是:某些编译器是否有可能优化掉那些对具有本地静态数据的函数(在这种情况下为构造函数)的调用?所以问题是究竟什么是“有副作用”,据我了解,这意味着编译器不允许对其进行优化。拥有函数局部静态数据是否足以让编译器认为函数调用不能被忽略?