查找C ++静态初始化顺序问题
我们遇到了一些静态初始化命令失败的问题,我正在寻找方法来梳理大量代码以查找可能的事件。 有关如何有效地做到这一点的任何建议?
编辑:我得到了一些关于如何解决静态初始化顺序问题的很好的答案,但这不是我的问题。 我想知道如何查找受此问题影响的对象。 在这方面埃文的答案似乎是迄今为止最好的; 我不认为我们可以使用valgrind,但我们可能有内存分析工具可以执行类似的功能。 只有在给定构建的初始化顺序错误的情况下,才会发现问题,并且顺序可能随着每个构建而改变。 也许有一个静态分析工具可以解决这个问题。 我们的平台是在AIX上运行的IBM XLC / C ++编译器。
解决初始化的顺序:
首先,这只是一个暂时的解决方法,因为你有全局变量,你试图摆脱但没有时间(你最终会摆脱它们是不是?):-)
class A
{
public:
// Get the global instance abc
static A& getInstance_abc() // return a reference
{
static A instance_abc;
return instance_abc;
}
};
这将保证它在首次使用时被初始化,并在应用程序终止时被销毁。
多线程问题:
C ++ 11确保这是线程安全的:
§6.7[stmt.dcl] p4
如果控制在变量初始化时同时进入声明,则并发执行应等待初始化完成。
但是,C ++ 03并没有正式保证静态函数对象的构造是线程安全的。 所以在技术上getInstance_XXX()
方法必须用关键部分来保护。 好的一面是,gcc有一个明确的补丁作为编译器的一部分,它保证每个静态函数对象只有在线程出现时才会初始化一次。
请注意:不要使用双重检查锁定模式来尝试和避免锁定的成本。 这在C ++ 03中不起作用。
创作问题:
在创作时,不存在任何问题,因为我们保证在创作之前创建它。
销毁问题:
在对象被销毁后存在访问该对象的潜在问题。 这只会发生,如果你从另一个全局变量的析构函数访问该对象(通过全局,我指的是任何非本地静态变量)。
解决方案是确保你强制销毁的顺序。
请记住,破坏顺序与构造顺序完全相反。 因此,如果您在析构函数中访问该对象,则必须保证该对象没有被销毁。 为此,您必须确保在构造调用对象之前完全构造该对象。
class B
{
public:
static B& getInstance_Bglob;
{
static B instance_Bglob;
return instance_Bglob;;
}
~B()
{
A::getInstance_abc().doSomthing();
// The object abc is accessed from the destructor.
// Potential problem.
// You must guarantee that abc is destroyed after this object.
// To guarantee this you must make sure it is constructed first.
// To do this just access the object from the constructor.
}
B()
{
A::getInstance_abc();
// abc is now fully constructed.
// This means it was constructed before this object.
// This means it will be destroyed after this object.
// This means it is safe to use from the destructor.
}
};
我只是写了一些代码来追踪这个问题。 我们有一个很好的代码库(1000多个文件),在Windows / VC ++ 2005上工作正常,但在Solaris / gcc上启动时崩溃。 我写了下面的.h文件:
#ifndef FIASCO_H
#define FIASCO_H
/////////////////////////////////////////////////////////////////////////////////////////////////////
// [WS 2010-07-30] Detect the infamous "Static initialization order fiasco"
// email warrenstevens --> [initials]@[firstnamelastname].com
// read --> http://www.parashift.com/c++-faq-lite/ctors.html#faq-10.12 if you haven't suffered
// To enable this feature --> define E-N-A-B-L-E-_-F-I-A-S-C-O-_-F-I-N-D-E-R, rebuild, and run
#define ENABLE_FIASCO_FINDER
/////////////////////////////////////////////////////////////////////////////////////////////////////
#ifdef ENABLE_FIASCO_FINDER
#include <iostream>
#include <fstream>
inline bool WriteFiasco(const std::string& fileName)
{
static int counter = 0;
++counter;
std::ofstream file;
file.open("FiascoFinder.txt", std::ios::out | std::ios::app);
file << "Starting to initialize file - number: [" << counter << "] filename: [" << fileName.c_str() << "]" << std::endl;
file.flush();
file.close();
return true;
}
// [WS 2010-07-30] If you get a name collision on the following line, your usage is likely incorrect
#define FIASCO_FINDER static const bool g_psuedoUniqueName = WriteFiasco(__FILE__);
#else // ENABLE_FIASCO_FINDER
// do nothing
#define FIASCO_FINDER
#endif // ENABLE_FIASCO_FINDER
#endif //FIASCO_H
并在解决方案中的每个 .cpp文件中添加了以下内容:
#include "PreCompiledHeader.h" // (which #include's the above file)
FIASCO_FINDER
#include "RegularIncludeOne.h"
#include "RegularIncludeTwo.h"
当你运行你的应用程序时,你会得到如下输出文件:
Starting to initialize file - number: [1] filename: [p:OneFile.cpp]
Starting to initialize file - number: [2] filename: [p:SecondFile.cpp]
Starting to initialize file - number: [3] filename: [p:ThirdFile.cpp]
如果遇到崩溃,罪魁祸首应该在列出的最后一个.cpp文件中。 至少,这会给你一个设置断点的好地方,因为这段代码应该是绝对的第一个执行代码(之后你可以遍历代码并查看所有正在初始化的全局变量) 。
笔记:
将“FIASCO_FINDER”宏放在尽可能靠近文件顶部的位置非常重要。 如果你把它放在一些其他的#includes之下,你会在识别你所在的文件之前冒着崩溃的风险。
如果您使用的是Visual Studio和预编译头文件,可以使用查找并替换对话框将这个额外的宏代码行添加到所有的.cpp文件中,以将现有的#include“precompiledheader.h”替换为相同的文本加FIASCO_FINDER行(如果勾选“正则表达式”,则可以使用“ n”插入多行替换文本)
根据您的编译器,您可以在构造函数初始化代码处放置一个断点。 在Visual C ++中,这是_initterm
函数,它给出了要调用的函数列表的开始和结束指针。
然后进入每个函数来获取文件和函数名称(假设你已经编译了调试信息)。 获得名称后,退出该函数(备份到_initterm
)并继续,直到_initterm
退出。
这给你所有的静态初始化器,而不仅仅是你的代码中的那些 - 这是获得详尽列表的最简单的方法。 您可以过滤掉无法控制的内容(例如第三方库中的内容)。
该理论适用于其他编译器,但函数的名称和调试器的功能可能会发生变化。
链接地址: http://www.djcxy.com/p/78843.html上一篇: Finding C++ static initialization order problems
下一篇: Singleton instance declared as static variable of GetInstance method