实现pimpl习语时链接器错误

时间:2011-09-30 21:26:53

标签: c++ linker pimpl-idiom

编辑提供者更清晰一点。抱歉让所有人感到困惑。

这是在Windows下。

我有一个使用pimpl习惯用法实现类的静态库。 pimpl标头不仅被消费代码使用,而且还链接到静态库。然而,当我编译消费代码(.exe)时,链接器会抱怨pimpl标头隐藏的实现类上未解析的外部。

这怎么可能?

// Foo.lib

// Foo.h

class FooImpl;

class Foo
{
    std::shared_ptr<FooImpl> pimpl_;    
public:
    Foo();
};

// Foo.cpp

#include "Foo.h"
#include "FooImpl.h"

Foo::Foo() : pimpl_(new FooImpl())
{
}

// This is how its used in Bar.exe
// Bar.exe links against Foo.lib

// Bar.h

#include "Foo.h"

class Bar
{
    Foo access_foo_;
};

// Bar.cpp

#include "Bar.h"

当我编译/链接Bar.exe时,链接器抱怨它无法解析FooImpl。我忘了它说的确切,因为我现在无法访问我的工作PC,但这是它的要点。这个错误对我没有意义,因为进入pimpl路线的目的是让Bar.exe不必担心FooImpl。

确切的错误是这样的:

1&gt; Foo.lib(Foo.obj):错误LNK2019:未解析的外部符号“public:__thiscall FooImpl :: FooImpl(void)”(?? 0FooImpl @@ QAE @ XZ)在函数“public:__thiscall Foo”中引用:: Foo(无效)“(?? 0Foo @@ QAE @ XZ)

2 个答案:

答案 0 :(得分:2)

创建静态库时,链接器不会尝试解析缺少的所有内容;它假设您稍后将其链接到另一个库,或者可执行文件本身将贡献一些缺少的功能。您必须忘记在库项目中包含一些关键的实现文件。

另一种可能性是pimpl实现类是模板类。模板不会立即生成代码,编译器会等到您尝试使用填充的模板参数时使用它们。您的实现文件必须包含模板的实例化,其中包含库支持的参数。

<小时/> 看到你的编辑后,问题是Foo :: Foo构造函数需要访问FooImpl :: FooImpl构造函数,但链接器不知道在哪里找到它。当链接器将库放在一起时,它不会尝试解析当时的所有引用,因为它可能依赖于另一个库。唯一需要解决的问题是它将可执行文件组合在一起。所以即使Bar不需要直接了解FooImpl,它仍然依赖于它。

您可以通过以下两种方式解决此问题:从库中导出FooImpl和Foo,或者确保Foo在编译时可以访问FooImpl,方法是将它们放在Foo.cpp中,FooImpl位于Foo之前​​。< / p>

答案 1 :(得分:2)

  

1&gt; Foo.lib(Foo.obj):错误LNK2019:未解析的外部符号“public:__thiscall FooImpl :: FooImpl(void)”(?? 0FooImpl @@ QAE @ XZ)在函数“public:__thiscall Foo”中引用:: Foo(无效)“(?? 0Foo @@ QAE @ XZ)

链接器抱怨你无法找到FooImpl::FooImpl()的定义。你在这里调用构造函数 -

Foo::Foo() : pimpl_(new FooImpl())
                    // ^^^^^^^^^ Invoking the default constructor.

您刚刚提供了默认构造函数的声明,但没有提供定义