PIMPL 习语 VS 前向声明

PIMPL idiom VS forward declaration

我已经阅读了一些关于 PIMPL 习语的内容并且想知道 - 转发声明依赖类型有什么不同吗?

如果是:

具体考虑依赖于Bar的class Foo(应该有一个Bar类型的成员)。

Foo.h 前向声明:

class Bar;

class Foo
{
public:
    Foo();

private:
    Bar* _bar;
};

Foo.h 与 PIMPL:

class Foo
{
    public:
        Foo();

    private:
        /* FooImpl is an incomplete type at this point.
         * Implemented in cpp file and has a member of type Bar.
         */
        class FooImpl;  

        FooImpl* _fooImpl;
}

请忽略原始指针的用法 - 我只是想说明一点

PIMPL 模式通常用于完全隐藏使用您的代码的实现细节 class。

例如,您的 class 可能包装了 platform-specific 功能。即使您使用前向声明 and/or 条件编译,您最终也会在 header 文件中公开 platform-specific 代码。这意味着任何使用你的 class 的代码在这些类型上也会以 header-dependency 结束,这意味着你的 class 可能会根据平台而改变(例如,大小可能是不同)。

PIMPL 模式可以让您将所有 platform-specific 细节隐藏在实现文件(通常是 *.cpp 或类似文件)中。这意味着程序中其他任何地方的其他代码都无法直接看到它,从而使您的包装器 class 在所有平台上保持干净和一致。

这只是 PIMPL 派上用场的例子之一,但它还有其他用途。

同样值得注意的是,并不是所有的东西都可以forward-declared,并且前向声明通常需要做出不受欢迎的让步,例如到处使用指针或引用。

I have read a bit about the PIMPL idiom and was wondering - is it any different to forward declaring the dependent type(s)?

是的,它们是不同的。 PIMPL idiom (it has several names) 专门用于隐藏客户端代码的实现细节。这样做的原因有很多,包括(但不限于);

  • 当 class 细节改变时隔离重建(它们是隐藏的)
  • 最小化必需的或冲突的 header 包含物
  • 总体构建时间减少
  • 最低导出要求(尽管抽象 classes 也可用于此目的)
  • 更容易控制因多个目标或平台而异的实施细节

本质上,PIMPL 提供了一种 "hide" 从客户端代码实现的技术 - 无论何时都需要。

When will I prefer using [PIMPL] over forward declaration?

这真的与意图有关 - 您的代码与抽象有关 - 照顾好这些抽象,滋养它们并保护它们,它们将为您提供良好的服务。

问题变成了 - 哪一个更能代表您的意图?我敢说 FooImpl 更好,我感觉到您的意图是向客户端隐藏 class 的实现,并且此实现更好地代表了该意图(因为 FooImpl 不是客户端可访问)。

如果您的意图是在代码的其他地方使用 Bar,在 class Foo 之外,那么该实现更好,因为这是意图并且该实现允许你这样做。

Do these two versions differ in their compilation time?

我对此表示怀疑。 BarFooImpl 的实现在它们定义的翻译单元之外是不可见的。

Is one of them more scalable than the other?

不是,不是。从某种一般意义上讲,代码越清晰,人们就越容易扩展它。