实现 pimpl idiom 时出现链接器错误
Posted
技术标签:
【中文标题】实现 pimpl idiom 时出现链接器错误【英文标题】:Linker error while implementing pimpl idiom 【发布时间】:2011-09-30 21:26:52 【问题描述】:对提供者进行了更清晰的编辑。很抱歉让大家感到困惑。
这是在 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。我忘记了它到底说了什么,因为我现在无法访问我的工作电脑,但这就是它的要点。这个错误对我来说没有意义,因为走 pimpl 路线的目的是让 Bar.exe 不必担心 FooImpl。
确切的错误是这样的:
1>Foo.lib(Foo.obj) : error LNK2019: unresolved external symbol "public: __thiscall FooImpl::FooImpl(void)" (??0FooImpl@@QAE@XZ) 在函数“public: __thiscall Foo”中引用::Foo(void)" (??0Foo@@QAE@XZ)
【问题讨论】:
第3行有错误;它应该说“foo”,而不是“bar”。 你在这里没有给我们太多机会 - 告诉我们你尝试了什么,或者向我们展示一些编译器输出,或者向我们展示你在文件顶部包含的内容,或者如果您使用了makefile,可能会显示makefile?我们可能可以帮助他们:) 【参考方案1】:当您创建静态库时,链接器不会尝试解决所有缺失的问题;它假定您稍后会将其链接到另一个库,或者可执行文件本身将提供一些缺失的功能。您一定忘记在库项目中包含一些关键的实现文件。
另一种可能是pimpl实现类是一个模板类。模板不会立即生成代码,编译器会一直等待,直到您尝试使用它们并填充模板参数。您的实现文件必须包含模板的实例化以及您的库将支持的参数。
看到您的编辑后,问题是 Foo::Foo 构造函数需要访问 FooImpl::FooImpl 构造函数,但链接器不知道在哪里找到它。当链接器将库放在一起时,它不会尝试解析所有引用,因为它可能依赖于另一个库。唯一需要解决所有问题的时间就是将可执行文件放在一起。所以即使 Bar 不需要直接了解 FooImpl,它仍然对它有依赖。
您可以通过以下两种方式之一解决此问题:要么将 FooImpl 与 Foo 一起从库中导出,要么确保 Foo 在编译时可以访问 FooImpl,方法是将它们都放在 Foo.cpp 中,而 FooImpl 位于 Foo 之前。
【讨论】:
为了更清楚,我编辑了我的问题。 FWIW,我的 pimpl 类不是模板。 @Dilip,你正在成为我的第一个断言的牺牲品——你的 Foo::Foo 无法访问 FooImpl::FooImpl。要么确保 FooImpl::FooImpl 从库中导出,要么在 Foo::Foo 之前将其放入 Foo.cpp。 非常感谢!我想我现在明白了。我明天上班时试试这个。 在 Foo.cpp 中包含 FooImpl 解决了链接器错误。我不想导出,因为从静态库中这样做有点违反直觉【参考方案2】:1>Foo.lib(Foo.obj) : error LNK2019: unresolved external symbol "public: __thiscall FooImpl::FooImpl(void)" (??0FooImpl@@QAE@XZ) 在函数“public: __thiscall Foo”中引用::Foo(void)" (??0Foo@@QAE@XZ)
链接器抱怨你找不到FooImpl::FooImpl()
的定义。你在这里调用构造函数-
Foo::Foo() : pimpl_(new FooImpl())
// ^^^^^^^^^ Invoking the default constructor.
您只是提供了默认构造函数的声明,但不是定义。
【讨论】:
我不明白。构建 Foo.lib 时不会发生此错误。这发生在我构建 Bar.exe(链接到 Foo.lib)时。这让我感到困惑。 Foo pimpl 类存在的主要原因是 Bar.exe 不必依赖 FooImpl,对吗? 我可能误用了编译防火墙的习语来实现接口-实现分离。我只为 Bar.exe 提供 Foo.h,并允许 Bar.exe 链接到 Foo.lib,希望这样可以避免 Bar.exe 担心 FooImpl。我好像弄错了?"I don't understand. This error is not happening when I build Foo.lib."
可能在您构建的文件中,您无法实例化调用默认构造函数的FooImpl
。以上是关于实现 pimpl idiom 时出现链接器错误的主要内容,如果未能解决你的问题,请参考以下文章