我的知识在链接DLL如何工作方面有点模糊,但我观察到对可执行文件中的静态成员变量的更改,该变量不会更改DLL中的相同静态成员变量。这是我的情景:
main.cpp静态链接到mylib.lib。在mylib.lib中,我有以下类:
// foo.h
class Foo
{
public:
static int m_global;
Foo();
~Foo();
};
和
// foo.cpp
#include "foo.h"
int Foo::m_global = 5;
我还有一个链接到mylib.lib的DLL,其中包含以下内容:
//testdll.h
#define MATHLIBRARY_API __declspec(dllimport)
void MATHLIBRARY_API printFoo();
和
// testdll.cpp
#include "testdll.h"
#include <iostream>
void printFoo() {
std::cout << Foo::m_global << std::endl;
}
最后,在我的可执行文件的main.cpp中
// main.cpp
#include <iostream>
#include "testdll.h"
#include "foo.h"
int main() {
std::cout << Foo::m_global << std::endl;
Foo::m_global = 7;
std::cout << Foo::m_global << std::endl;
printMutiply();
return 0;
}
我的预期输出是5,7,7。但是,我看到5,7,5这告诉我DLL没有看到静态成员变量更改。为什么会这样?如何让DLL看到可执行文件中的静态成员变量的变化?
答案 0 :(得分:3)
信不信由你,但是你的应用程序违反了一个定义规则,因此触发了未定义的行为。您的program
(在C ++标准中称为)最终具有Foo::m_global
的双重定义 - 一个在可加载库中,另一个在main
内。作为此未定义行为的可观察效果,Microsoft动态加载程序创建两个符号,一个来自可加载对象,另一个来自main。
在Linux中,ld
(linux loader)实际上会将这些符号合并为一个(反过来会触发对非平凡对象的双重破坏)。
底线 - 不要在可加载库和可执行文件之间共享全局符号的定义。这适用于函数和变量,但共享函数通常没有明显的副作用,尽管技术上也是未定义的行为。
答案 1 :(得分:0)
这就是我们在项目中处理它的方式:
/* First a general definition which covers differences
* of Windows and Linux for all of your libraries:
*/
#ifdef _WIN32
/* for Windows, Visual C++ */
#define MY_EXPORT __declspec(dllexport)
#define MY_IMPORT __declspec(dllimport)
#else /* _WIN32 */
/* for gcc */
#define MY_EXPORT __attribute__((visibility("default")))
#define MY_IMPORT __attribute__((visibility("default")))
#endif /* _WIN32 */
这必须为您的每个图书馆准备:
/* The API macro to distiguish two cases:
* 1. DLL/Shared Object
* 2. usage of DLL/Shared Object
*/
#ifdef BUILD_MY_LIB
#define MY_LIB_API MY_EXPORT
#else /* BUILD_MY_LIB */
#define MY_LIB_API MY_IMPORT
#endif /* BUILD_MY_LIB */
然后在从MyLib库导出的任何MY_LIB_API
中使用 class
:
class MY_LIB_API Foo {
};
其余的是使用编译器参数完成的:
要编译MyLib DLL或共享对象,-DBUILD_MY_LIB
将添加到编译器的命令行参数中。
要使用DLL或共享对象,无需其他设置。
实际上,我们的解决方案也考虑了静态库。它将MY_LIB_API
定义为空。但是,我们已经很久没有使用它了。因此,我把这部分留了下来。 (我必须承认我忘了它的工作原理......)