起源:因为发现插件析构函数老是不被调用,最终注意到该bug。
简单陈述一些东西,不做加工
该BUG内容:
Manual 中说:程序结束时,插件会自动被unload,因此不需要手动调用QPluginLoader::unload ()
bool QPluginLoader::unload () Unloads the plugin and returns true if the plugin could be unloaded; otherwise returns false. This happens automatically on application termination, so you shouldn't normally need to call this function.
struct LibraryData { LibraryData() : settings(0) { } ~LibraryData() { foreach(QLibraryPrivate *lib, loadedLibs) { lib->unload(); delete settings; } QSettings *settings; LibraryMap libraryMap; }; Q_GLOBAL_STATIC(LibraryData, libraryData)
它是借助于LibraryData的析构函数实现的。
LibraryData() : settings(0) { } ~LibraryData() { delete settings; - foreach(QLibraryPrivate *lib, loadedLibs) { - lib->unload(); - } }
看看插件的 new 和 delete 分别藏身何处
所有的插件我们都会使用下面这个宏:
Q_EXPORT_PLUGIN2(PluginName, ClassName)
PluginName 可以任意取,它只对静态插件有意义。
ClassName 是要导出的插件类的类名
该宏展开后有new ClassName(详见Qt插件学习一):
extern "C" Q_DECL_EXPORT QObject* qt_plugin_instance() { static QPointer<QObject> _instance; if (!_instance) _instance = new ClassName; return _instance; }
bool QLibraryPrivate::loadPlugin() { instance = (QtPluginInstanceFunction)resolve("qt_plugin_instance");
而后调用
QObject *QPluginLoader::instance() { ... return d->instance();
调用unload时,先调用 delete 删除插件对象
bool QLibraryPrivate::unload() { if (!libraryUnloadCount.deref()) { // only unload if ALL QLibrary instance wanted to if (instance) delete instance(); if (unload_sys()) { instance = 0; pHnd = 0; } }
乱七八糟,罗列与此:
在 QTextCodec相关的new、delete问题一则 一文中,QTextCodec 插件所用的方法或许也可以借鉴一下。
不过看看QTBUG-4341,问题还是好复杂啊
C++中的 Storage Druation 分4种:
由于thread那个目前尚没人用,所以static storage duration应该是最复杂的一个。也在Qt中也是大量使用的一个。
不过Google的C++ Style Guide却并不建议对类对象使用这个东西:
Static or global variables of class type are forbidden: they cause hard-to-find bugs due to indeterminate order of construction and destruction.
Objects with static storage duration, including global variables, static variables, static class member variables, and function static variables, must be Plain Old Data (POD): only ints, chars, floats, or pointers, or arrays/structs of POD.
The order in which class constructors and initializers for static variables are called is only partially specified in C++ and can even change from build to build, which can cause bugs that are difficult to find. Therefore in addition to banning globals of class type, we do not allow static POD variables to be initialized with the result of a function, unless that function (such as getenv(), or getpid()) does not itself depend on any other globals.
Likewise, the order in which destructors are called is defined to be the reverse of the order in which the constructors were called. Since constructor order is indeterminate, so is destructor order. For example, at program-end time a static variable might have been destroyed, but code still running -- perhaps in another thread -- tries to access it and fails. Or the destructor for a static 'string' variable might be run prior to the destructor for another variable that contains a reference to that string.
As a result we only allow static variables to contain POD data. This rule completely disallows vector (use C arrays instead), or string (use const char []).
If you need a static or global variable of a class type, consider initializing a pointer (which will never be freed), from either your main() function or from pthread_once(). Note that this must be a raw pointer, not a "smart" pointer, since the smart pointer's destructor will have the order-of-destructor issue that we are trying to avoid.
对于static storage duration的对象:
C++ 标准对涉及到动态库的东西似乎没有任何规定。
http://msdn.microsoft.com/en-us/library/988ye33t.aspx提到:
Each time a new process attempts to use the DLL, the operating system creates a separate copy of the DLL's data: this is called process attach. The run-time library code for the DLL calls the constructors for all of the global objects, if any, and then calls the DllMain function with process attach selected. The opposite situation is process detach: the run-time library code calls DllMain with process detach selected and then calls a list of termination functions, including atexit functions, destructors for the global objects, and destructors for the static objects. Note that the order of events in process attach is the reverse of that in process detach.
https://bugreports.qt.nokia.com/browse/QTBUG-17458
http://msdn.microsoft.com/en-us/library/988ye33t.aspx
http://stackoverflow.com/questions/75701/what-happens-to-global-variables-declared-in-a-dll
http://blog.csdn.net/dbzhang800/article/details/6615612