如果标题不言自明,我深表歉意。我试图了解为什么我的单例工厂模式无法正常工作,并且在使用库与链接单个对象文件时遇到了奇怪的差异。
这是代码的简化版本:
主要.cpp
#include <iostream>
#include "bar.hpp"
int main (int /*argc*/, char** /*argv*/)
{
A::get().print();
return 0;
}
酒吧.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)
,然后在主函数中使用相同的 A 实例化,这因此打印 2
。在第二种情况下,对 baz.hpp 中的 A::get().set(2)
的调用永远不会发生,并且在 main 函数中由构造函数设置的值(即 0
) 被打印出来。
所以最后我可以问我的问题:为什么两种情况下的行为不同?我希望要么都打印 0 一次,要么打印 2 两次。我一直认为库只是传送目标文件的一种紧凑方式,并且链接 mylib.a 的行为应该与直接链接 baz.o 的行为相同。为什么不是这样?
编辑:正如 Richard 所解释的,原因是 main.cpp 中不需要 baz.cpp 中定义的符号,因此 baz.o 不会从库中提取并链接。这就提出了另一个问题:是否有解决方法来确保指令 A::get().set(2)
被执行?我想避免使单例成为全局对象,但我不确定这是否可能。我还想避免在 main 中包含 baz.hpp,因为可能有很多 bazxyz.hpp
并且这需要 main.cpp 提前知道所有这些,违背了整个目的类似工厂的注册过程...
最佳答案
如果这是一个静态库,那么某个地方的某个模块将不得不在将要向工厂注册的对象的每个实现文件中解决一些问题。
一个合理的位置是 bar.cpp
(您还没有这个文件)。它将包含 A
的部分或全部实现以及一些调用您将要创建的小部件的注册函数的方法。
self 发现只有在目标文件链接到可执行文件时才有效。这使 c++ 启动序列有机会了解和构造具有全局链接的所有对象。
关于c++ - 跨编译单元的单例 : linking library vs linking objects,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41187986/