4

我在 C++ 共享库项目中遇到全局变量问题。我的库必须作为标准 g++ 共享库 (.so) 以及 dll 工作。我通过创建文件 libiup_dll.cpp 和 libiup_dll.h 来做到这一点,我有类似的东西

#ifdef BUILD_DLL

// code for the dll: wrapper functions around the classes in my shared library

#endif

在我的 dll 中,我需要函数 setloglevel(int) 和 geterrormsg()。在我的所有课程中,我会将所有错误消息附加到全局变量 errormsg 中。然后应该由 geterrormsg() 函数返回此变量。我通过使用实现了这一点

std::string errormsg;
int loglevel;

在 libiup_dll.h (外部和#ifdefs,所以它应该是全局可用的),然后把

extern std::string errormsg;
extern int loglevel;

在我的班级的 .h 文件中(在班级之外,在文件的顶部)

现在我有两个问题:

1) 使用我的库使用 g++ 编译命令行程序时,出现错误

构建目标:libiup_test 调用:GCC C++ Linker g++ -L"/home/hilboll/src/libiup/Release" -L/usr/local/lib -o"libiup_test" ./src/stratcalc/SimpleStratosphericColumnCalculatorTest.o ./src/ interp/SimpleInterpolatorTest.o ./src/Test.o -lgsl -lhdf5 -lhdf5_cpp -lblas -liup /home/hilboll/src/libiup/Release/libiup.so: undefined reference to loglevel' /home/hilboll/src/libiup/Release/libiup.so: undefined reference toerrormsg' collect2: ld 返回 1 退出状态 make : *** [libiup_test] 错误 1

即使在我的命令行程序中,也没有任何对 errormsg 或 loglevel 的引用。

2)当试图用VS2008在windows下编译dll时,我得到了

z:\src\vs\libiup_dll\libiup_dll.h(229) : 错误 C2086: 'std::string errormsg': Neudefinition z:\src\libiup\src\stratcalc../interp/SimpleInterpolator.h(16): Siehe Deklaration von 'errormsg' z:\src\vs\libiup_dll\libiup_dll.h(234) : error C2086: 'int loglevel': Neudefinition z:\src\libiup\src\stratcalc../interp/SimpleInterpolator.h( 17): Siehe Deklaration von 'loglevel'

据我了解,这意味着 VS 认为我将这两个变量定义了两次。但是,在 SimpleInterpolator.h 16/17 中,只有 extern 声明......

似乎我还没有理解全局变量是如何工作的。任何帮助是极大的赞赏!

4

4 回答 4

4

诀窍是要知道每个 .cpp 文件都是一个编译单元——即被编译的东西。每个人都是一个完整的个体,彼此一无所知。仅在链接阶段将这些组合在一起。

现在,extern 对已编译的 cpp 文件说“这个变量可以在其他地方找到”,编译器只是向链接器放置一个引用提示来解决问题。

因此,当您将变量定义放在头文件中时,您很可能将该头文件包含在 2 个(或更多)cpp 文件中。因此,这些 cpp 文件中的每一个在编译时都认为它们具有真正的变量。然后链接器出现并看到太多。

将变量放入它们自己的 cpp 文件中(或放入主 cpp 文件中),并且只将外部引用放在头文件中。那么你应该可以使用多重定义的符号。

于 2009-08-24T15:38:10.453 回答
4

全局变量可以声明多次,但只能定义一次。

“extern”关键字将原本定义为声明的内容标记。

您如何执行此操作的一般习惯用法如下:

// In a header file (declaration)
extern int myGlobal;

// In a source file (definition)
int myGlobal;

然后,在你想引用全局的地方,你应该重复 extern 声明,或者包含已经有 extern 声明的头文件。

您绝对应该做的是将“int myGlobal”定义(没有“extern”)放入标题中。如果这样做,那么包含该头文件的每个文件都将尝试定义自己的 myGlobal,并且在链接时最终会出现符号冲突。定义应该在一个且只有一个源文件中。extern 声明可以出现在任意数量的文件或标题中。

特别是,您的两个错误发生的情况是:

  • 当您在 Linux 下编译时,定义全局变量的 DLL 标头会被您的预处理器宏消除,因此您的全局变量有一个声明但没有定义,并且链接器会报错。
  • 在 Windows 下编译时,DLL 头包含在多个编译单元(源文件)中,因此全局变量有多个定义,链接器会报错。
于 2009-08-24T15:35:46.383 回答
1

你说你有:

std::string errormsg;
int loglevel;

libiup_dll.h外面任何#ifdefslibiup_dll.h如果包含多次(直接或间接),这是一个问题,并且可能是您的问题 #2 的原因。

我认为如果包含标头,这也可能是问题 #1 的原因 - 即使您可能不会在其他地方使用它们,您也拥有由标头引入的变量的定义。

这两个变量的定义通常需要在 .c 或 .cpp 文件中,而不是在头文件中。在extern标题中声明这些是可以的。

于 2009-08-24T15:36:18.787 回答
1

你说,

I implemented this by using

    std::string errormsg;
    int loglevel;

in libiup_dll.h (outside and #ifdefs, so it should be globally available),
and then putting

    extern std::string errormsg;
    extern int loglevel;

in my classes' .h files (outside the class, at the top of the files)

相反,我认为您应该在任意数量的头文件中声明变量extern,然后在唯一的 C 或 CPP 文件中定义没有 extern 的变量。

于 2009-08-24T15:36:35.680 回答