代码之家  ›  专栏  ›  技术社区  ›  Tobias

你用什么模式来解耦C++中的接口和实现?

  •  8
  • Tobias  · 技术社区  · 15 年前

    大型C++项目中的一个问题是构建时间。在您的依赖树中有一些类位于高处,您需要对其进行处理,但通常您避免这样做,因为每个构建都需要很长的时间。不一定要更改其公共接口,但可能要更改其私有成员(添加缓存变量、提取私有方法…)。您面临的问题是,在C++中,甚至私有成员都在公共头文件中声明,因此您的构建系统需要重新编译所有内容。

    你在这种情况下做什么?

    我已经草拟了两个我知道的解决方案,但它们都有它们的缺点,也许还有一个更好的方案我还没有想到。

    6 回复  |  直到 15 年前
        1
  •  9
  •   Tobias    15 年前

    PIMPL模式:

    在头文件中,只声明公共方法和私有指针(PIMPL指针或委托)到前向声明的实现类。

    在源代码中,声明实现类,将公共类的每个公共方法转发给委托,并在公共类的每个构造函数中构造PIMPL类的实例。

    加:

    • 允许您更改类的实现,而不必重新编译所有内容。
    • 继承可以很好地工作,只是语法会有所不同。

    负数:

    • 很多愚蠢的方法机构写来做代表团。
    • 调试有点尴尬,因为您有大量的委托要执行。
    • 您的基类中有一个额外的指针,如果您有许多小对象,这可能是一个问题。
        2
  •  6
  •   Greg Hewgill    15 年前

    John Lakos Large Scale C++ Software Design 是一本很好的书,解决了构建大型C++项目所涉及的挑战。这些问题和解决办法都是根据实际情况而定的,当然,以上问题也会作详细的讨论。强烈推荐。

        3
  •  2
  •   Tobias    15 年前

    使用继承:

    在头中,将公共方法声明为纯虚拟方法和工厂。

    在源代码中,从接口派生一个实现类并实现它。在工厂的实现中返回一个实现的实例。

    加:

    • 允许您更改类的实现,而不必重新编译所有内容。
    • 简单易行。

    负数:

    • 定义公共基类的(public)派生实例真的很难,该实例应该继承公共基(private)实现的某些方法。
        4
  •  0
  •   Remy Lebeau    15 年前

    您可以对类A使用前向声明,该声明由另一个类B中的指针引用。然后,您可以将类的头文件包含在类B的实现文件中,而不是其头文件中。这样,对类A所做的更改不会影响包含类B头文件的源文件。任何想要访问类A成员的类都必须包含类A的头文件。

        5
  •  0
  •   Shing Yip    15 年前

    重构和使用pimpl/handle-body习惯用法,使用纯虚拟接口隐藏实现细节似乎是最流行的答案。在设计大型系统时,应该考虑编译时间和开发人员的生产力。但是,如果你在现有的大型C++系统上工作,没有单元测试覆盖率呢?重构通常是不可能的。

    当我不希望编译器在接触到一些常见的头文件后编译这个世界时,我通常会做的是让一个makefile/script只编译我知道需要重新编译的文件。例如,如果我要向一个类添加一个非虚拟私有函数,那么即使它的头文件包含在一百个其他文件中,也只需要重新编译该类的cpp文件。在我离开之前,我开始了一个干净的建设来重建世界。

        6
  •  0
  •   Daniel Daranas    15 年前

    一个也没有。

    我在使用一个中看到了这一点,但我认为以下论点在许多场景中都可以缓解这一点:

    1. 清晰是第一位的。如果影响运行时的清晰度 速度必须考虑两次,什么? 关于为了 编译时间 速度?
    2. 私人成员不应该经常改变。
    3. 通常不需要 那个 渴望重建一切。
    4. 未来将出现更快的工具,因此编译速度问题将自动得到缓解。您的代码不会自动变得更清晰。
    5. 无论如何,你应该经常重建。
    6. 你试过了吗? Incredibuild ?

    当然,最终这是一个经济决定。如果“3”的权重在您的项目中很重要,并且由于某些原因“6”不能应用,那么继续进行:使用这些模板您将获得比损失更多的收益。