跨编译单元的单例:链接库与链接对象

时间:2016-12-16 15:51:55

标签: c++ singleton static-linking

如果标题不完全不言自明,我道歉。我试图理解为什么我的单件工厂模式不能正常工作,并且在使用库与链接单个对象文件时遇到了一个奇怪的区别。

以下是代码的简化版本:

的main.cpp

#include <iostream>
#include "bar.hpp"

int main (int /*argc*/, char** /*argv*/)
{
  A::get().print();
  return 0;
}

bar.hpp

#ifndef BAR_HPP
#define BAR_HPP

#include <iostream>

class A
{
public:
  static A& get ()
  {
    static A a;
    return a;
  }
  bool set(const int i)
  {
    m_i = i;
    print();
    return true;
  }
  void print ()
  {
    std::cout << "print: " << m_i << "\n";
  }
private:

  int m_i;
  A () : m_i(0) {}
};
#endif // BAR_HPP

baz.hpp

#ifndef BAZ_HPP
#define BAZ_HPP

#include "bar.hpp"

namespace
{
static bool check = A::get().set(2);
}

#endif // BAZ_HPP

baz.cpp

#include "baz.hpp"

现在,我以两种方式构建我的“项目”:

生成文件:

all:
  g++ -std=c++11 -c baz.cpp
  g++ -std=c++11 -o test main.cpp baz.o
lib:
  g++ -std=c++11 -c baz.cpp
  ar rvs mylib.a baz.o
  g++ -std=c++11 -o test main.cpp mylib.a

以下是我得到的输出:

$ make all
$ ./test
print: 2
print: 2

$ make lib
$ ./test
print: 0

在第一种情况下,在baz.hpp中调用A::get().set(2),然后在main函数中使用相同的A实例,因此打印2。在第二种情况下,baz.hpp中对A::get().set(2)的调用永远不会发生,而在main函数中,将打印构造函数设置的值(即0)。

所以最后我可以问我的问题:为什么两种情况下的行为不同?我希望既可以打印0次也可以打印2次。我总是认为库只是一种运送目标文件的紧凑方式,并且链接mylib.a的行为应该与直接链接baz.o的行为相同。为什么不是这样的?

编辑:正如理查德所解释的那样,原因是main.cpp中不需要在baz.cpp中定义符号,因此baz.o不会从库中提取并链接。这提出了另一个问题:是否有解决方法来确保执行指令A::get().set(2)?我想避免让单身人士成为一个全球性的对象,但我不确定这是可能的。我还想避免在主要中包含baz.hpp,因为可能有许多bazxyz.hpp并且需要main.cpp提前知道所有这些,违反工厂类注册的整个目的过程...

1 个答案:

答案 0 :(得分:1)

如果这是一个静态库,那么某个模块某个地方必须在将要向工厂注册的对象的每个实现文件中解决一些问题。

合理的地方是bar.cpp(这是一个你还没有的文件)。它将包含A的部分或全部实现,以及一些调用注册函数的方法,即您将要创建的小部件。

仅当目标文件链接到可执行文件时,自我发现才有效。这使得c ++启动序列有机会了解并构造具有全局链接的所有对象。