本地静态变量被多次实例化,为什么?

mon*_*ani 5 c++ c++11

我对从这段代码得到的结果感到困惑.在一个dll中,当静态变量初始化时,计数器递增.然后当执行main时我读了这个计数器,但我得到的是0而不是1.有人可以向我解释一下吗?

在我的动态库项目中:

// Header file
class Foo {
   int i_ = 0;

   Foo(const Foo&) = delete;
   Foo& operator= (Foo) = delete;

   Foo()
   {
   }

public:
   void inc()
   {
      ++i_;
   }

   int geti()
   {
      return i_;
   }

   static Foo& get()
   {
      static Foo instance_;
      return instance_;
   }

   Foo( Foo&&) = default;
   Foo& operator= (Foo&&) = default;
};

int initialize()
{
   Foo::get().inc();
   return 10;
}

class Bar
{
   static int b_;

};

// cpp file
#include "ClassLocalStatic.h"


int Bar::b_ = initialize();
Run Code Online (Sandbox Code Playgroud)

在我的应用项目中

// main.cpp
#include <iostream>

#include "ClassLocalstatic.h"

int main(int argc, const char * argv[])
{
   std::cout << Foo::get().geti();
   return 0;
}
Run Code Online (Sandbox Code Playgroud)

Mar*_*som 8

可执行文件和DLL都将获得自己的副本Foo::get(),每个副本都有自己的静态变量副本.因为它们位于单独的链接器输出中,所以链接器无法像通常那样合并它们.

进一步扩展:

C++规范允许在多个翻译单元中定义内联函数,只要它们都具有相同的主体; 将函数放在头文件中是完全可以的,因为它确保每个副本都是相同的.请参阅/sf/answers/293558891/.如果内联函数中存在静态变量,则编译器和链接器需要协同工作以确保在所有这些变量之间只使用一个副本.我不确定确切的机制,但无关紧要,标准要求它.不幸的是,链接器在生成输出可执行文件或DLL之后停止,并且它无法告知两个位置都存在该函数.

修复方法是将Foo::get()标题移出标题并将其放入仅在DLL中的源文件中.


Nic*_*las 7

C++规则规定内联函数定义可以与静态局部变量一起正常工作.也就是说,如果你内联函数定义,任何局部静态变量都将引用同一个变量.

但是,C++没有定义一件事:DLL.

C++规范完全不了解DLL; 它不知道如何处理它们.C++是根据静态链接定义的,而不是动态的.

因此,这意味着在处理DLL边界时规范不再适用.这就是你的问题所在.

虽然C++要求具有本地静态变量的内联函数仍然有效,但是不了解DLL的C++意味着所有内容都取决于编译器决定做什么.

对于跨DLL边界拆分的内联函数来说,没有本地静态变量按预期工作是完全合法的编译器行为.这是一个异常情况,我严重怀疑任何编译器开发人员花时间编写这样的可能性.

编译器要做的最合理的事情就是如果在DLL头中声明了一个extern全局变量它会做什么:每个DLL和可执行文件都有一个单独的变量.这就是为什么你需要特殊的语法来说明一个定义应该由这个可执行文件/ DLL(__declspec(dllexport))定义,以及来自其他可执行文件/ DLL(__declspec(dllimport))的内容.

您必须始终小心跨越DLL边界的内容.通常,不要像这样跨越DLL边界内联事物.