静态库中的符号有时会被链接到可执行文件中,有时则不会。

3
我有一个静态库,是在Linux上使用g++从许多cpp文件生成的。一个头文件包含一个实现工厂模式的类。
以下是头文件中的伪代码:
class Factory
{
public:
    static Factory& instance();
    Base * create(const std::string& name);
    template<class T>
      void register_class(const std::string& name);
}

template <class T>
class FactoryRegister
{
public:
    FactoryRegister(const std::string& name)
    {
       Factory::instance().register_class<T>(name);
    }
}

Factory的cpp文件有实现。在另一个cpp文件Derive.cpp中,我想要将一个类注册到Factory中。我定义了一个全局变量来实现这个。代码如下:
FactoryRegister<Derive> g_register_derive("derive");

所有这些文件都被编译成一个静态库,并链接到一个可执行文件中。
我的理解是,由于 g_register_derive 没有被任何代码引用,除非提供了整个存档选项,否则它不应该被链接到可执行文件中。
奇怪的是,如果我把 g_register_derive 放在 Derive.cpp 中,确实这个符号就不会被链接到可执行文件中。但是如果我把 g_register_derive 放在 Factory.cpp 中,它就会被链接到可执行文件中。
我使用 nm 来验证结果,并且还有一行代码调用 Factory::instance().create("Derive"),也可以用来检查 g_register_derive 是否已被链接。
当然,如果我提供了整个存档选项,g_register_derive 将始终被链接到可执行文件中。
1个回答

2
请查看 Stackoverflow 关于静态库的标签百科以了解与静态库相关的链接,特别是默认情况下,静态库中的对象文件libx.a(p.o)仅在链接器需要时才会被提取和链接到程序中。
如果链接器需要链接存档成员libx.a(p.o)以解析对已在该对象文件中定义符号foo的任何先前引用,则对于在libx.a(p.o)中还定义的任何其他符号bar的定义也将链接到程序中-因为它是该对象文件的一部分-无论程序是否引用bar
因此,如果您在源文件p.cpp中定义g_register_derive,该文件会被编译为p.o并作为libx.a(p.o)归档,而您的应用程序需要链接libx.a(p.o) 出于任何原因,那么默认情况下1g_register_derive 将被定义为你的程序。
如果将g_register_derive的定义移动到编译并归档为libx.a(q.o)q.cpp中,并且您的应用程序不需要出于任何原因链接libx.a(q.o),那么g_register_derive将不会定义在您的程序中。

[1] 通过非默认的编译和链接选项,您可以从程序中删除未使用的符号定义。请参见如何使用GCC和ld删除未使用的C / C ++符号?和接受的答案。


谢谢,这个问题困扰了我好几天。 - Lei Yu

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接