我在静态初始化方面遇到了一些奇怪的问题。我正在使用代码生成器为我编写的消息传递系统生成结构和序列化代码。为了能够根据消息ID轻松分配消息,我的代码生成器为每种消息类型输出类似于以下内容的内容:
MessageAllocator s_InputPushUserControllerMessageAlloc(INPUT_PUSH_USER_CONTROLLER_MESSAGE_ID, (AllocateMessageFunc)Create_InputPushUserControllerMessage);
MessageAllocator类基本上如下所示:
MessageAllocator::MessageAllocator( uint32_t messageTypeID, AllocateMessageFunc func )
{
if (!s_map) s_map = new std::map<uint32_t, AllocateMessageFunc>();
if (s_map->insert(std::make_pair(messageTypeID, func)).second == false)
{
//duplicate key!
ASSERT(false, L"Nooooo!");
}
s_count++;
}
MessageAllocator::~MessageAllocator()
{
s_count--;
if (s_count == 0) delete s_map;
}
其中s _
map和s _
count是MessageAllocator的静态成员。这大部分时间都有效,但有时消息不会添加到地图中。例如,除非我在启动代码中的某处调用Create _
InputPushUserControllerMessage(),否则不会添加此特定消息,但是其他消息可以正常工作。我认为这可能与链接器错误地认为类型未引用并删除它有关,因此我使用/ OPT:NOREF开关禁用了它(我使用的是Visual Studio 2008 SP1)但是没有效果。
我知道“静态初始化顺序惨败”的问题,但据我所知,创建这些对象的顺序不应该改变结果,所以这对我来说似乎没问题。
我们将不胜感激。
答案 0 :(得分:2)
将静态放入类中,使其成为类
的静态成员struct InputPushUserControllerMessageAlloc { static MessageAllocator s_obj; };
MessageAllocator InputPushUserControllerMessageAlloc::s_obj(
INPUT_PUSH_USER_CONTROLLER_MESSAGE_ID,
(AllocateMessageFunc)Create_InputPushUserControllerMessage);
标准允许它延迟具有命名空间范围的对象的初始化,直到使用来自其翻译单元的任何函数/对象。如果初始化有副作用,则无法优化。但这并不禁止推迟它。
具有类范围的对象不是这样。所以这可能会禁止它在那里进行优化。
答案 1 :(得分:1)
我会将s_map从静态类成员更改为静态方法成员:
std::map<uint32_t,AllocateMessageFunc>& MessageAllocator::getMap()
{
// Initialized on first use and destroyed correctly on program termination.
static std::map<uint32_t,AllocateMessageFunc> s_map;
return s_map;
}
MessageAllocator::MessageAllocator( uint32_t messageTypeID, AllocateMessageFunc func )
{
if (getMap().insert(std::make_pair(messageTypeID, func)).second == false)
{
//duplicate key!
ASSERT(false, L"Nooooo!");
}
}
无需析构函数或计数。
如果您的全局对象位于延迟加载的单独DLL(或共享库)中。 这可能会导致与您的描述类似的问题。
答案 2 :(得分:0)
您没有将指针设置回null。
MessageAllocator::~MessageAllocator()
{
s_count--;
if (s_count == 0)
{
delete s_map;
s_map = 0;
}
}
答案 3 :(得分:0)
事实证明链接器不包含包含静态初始值设定项的目标文件,因为没有引用它们中的任何函数。为了解决这个问题,我在extern“C”中创建了一个生成的函数,以便它具有可预测的非破坏名称,然后使用每个消息的pragma强制引用它
#pragma comment(linker, "/include:Create_GraphicsDynamicMeshCreationMessage")
我将生成的头文件放入后面包含在所有其他非生成文件中。它只是MSVC而且是一种黑客攻击,但我认为一旦我最终移植它就可以在GCC上做类似的事情。