【问题标题】:problems with global variables in shared library project (C++)共享库项目中的全局变量问题(C++)
【发布时间】:2009-08-24 15:25:49
【问题描述】:

我在 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++ 链接器 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: 未定义对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': 新定义 z:\src\libiup\src\stratcalc../interp/SimpleInterpolator.h(16): Siehe Deklaration von 'errormsg' z:\src\vs\libiup_dll\libiup_dll.h(234) : 错误 C2086: 'int loglevel': 新定义 z:\src\libiup\src\stratcalc../interp/SimpleInterpolator.h(17): Siehe Deklaration von 'loglevel'

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

似乎我还没有理解全局变量是如何工作的。非常感谢任何帮助!

【问题讨论】:

  • 使用会说英语的编译器可能是一个好的开始。
  • 显示实际来源而不是描述它会很好。

标签: c++ shared-libraries global-variables


【解决方案1】:

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

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

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

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

【讨论】:

    【解决方案2】:

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

    “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 头包含在多个编译单元(源文件)中,因此全局变量有多个定义,链接器报错。

    【讨论】:

      【解决方案3】:

      你说你有:

      std::string errormsg;
      int loglevel;
      

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

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

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

      【讨论】:

        【解决方案4】:

        你说,

        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的变量,然后定义不带extern的变量在一个且唯一-一个 C 或 CPP 文件。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2014-05-17
          • 1970-01-01
          • 1970-01-01
          • 2012-06-10
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多