PIMPL 成语 VS 前向声明

PIMPL idiom VS forward declaration

本文关键字:声明 VS 成语 PIMPL      更新时间:2023-10-16

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

㞖:

  • 我什么时候更喜欢使用它而不是前向声明?
  • 这两个版本的编译时间是否不同?
  • 其中一个是否比另一个更具可扩展性?

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

Foo.h与前向声明:

class Bar;
class Foo
{
public:
    Foo();
private:
    Bar* _bar;
};

与PIMPL Foo.h

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 习语的信息,并且想知道 - 转发声明依赖类型有什么不同吗?

是的,它们是不同的。PIMPL 习惯用法(它有几个名称)专门用于从客户端代码中隐藏实现详细信息。这样做可能有多种原因,包括(但不限于);

  • 在类详细信息更改时隔离重建(它们被隐藏)
  • 最大限度地减少必需或冲突的标头包含
  • 一般构建时间缩短
  • 最低导出要求(尽管抽象类也可以用于此目的)
  • 更轻松地控制因多个目标或平台而异的实施细节

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

我什么时候更喜欢使用 [PIMPL] 而不是前向声明?

这实际上是关于意图的 - 你的代码是关于抽象的 - 照顾这些抽象,培育和保护它们,它们将很好地为你服务。

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

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

这两个版本的编译时间是否不同?

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

其中一个是否比另一个更具可扩展性?

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

PIMPL 模式通常用于完全隐藏使用类的代码的实现详细信息。

例如,您的类可能正在包装特定于平台的功能。即使使用了前向声明和/或条件编译,最终也会在头文件中公开特定于平台的代码。这意味着使用您的类的任何代码最终也会依赖于这些类型的标头,这意味着您的类可能会根据平台而更改(例如,大小可能不同)。

PIMPL 模式允许您将所有特定于平台的详细信息隐藏在实现文件中(通常为 *.cpp 或类似文件)。这意味着程序中其他任何地方的其他代码都无法直接看到它,从而在所有平台上保持包装类的干净和一致。

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

还值得注意的是,并非所有内容都可以向前声明,并且前向声明通常需要做出不希望的让步,例如在任何地方使用指针或引用。