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

发布于 2024-07-24 09:16:00 字数 260 浏览 14 评论 0原文

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

在这种情况下你会怎么做?

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

One problem in large C++ projects can be build times. There is some class high up in your dependency tree which you would need to work on, but usually you avoid doing so because every build takes a very long time. You don't necessarily want to change its public interface, but maybe you want to change its private members (add a cache-variable, extract a private method, ...). The problem you are facing is that in C++, even private members are declared in the public header file, so your build system needs to recompile everything.

What do you do in this situation?

I have sketched two solutions which I know of, but they both have their downsides, and maybe there is a better one I have not yet thought of.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

撩动你心 2024-07-31 09:16:00

pimpl 模式:

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

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

另外:

  • 允许您更改类的实现,而无需重新编译所有内容。
  • 继承效果很好,只是语法略有不同。

缺点:

  • 需要编写很多很多愚蠢的方法体来进行委托。
  • 调试起来有点尴尬,因为你有大量的代表需要逐步执行。
  • 基类中的额外指针,如果您有很多小对象,这可能会成为问题。

The pimpl pattern:

In your header file, only declare the public methods and a private pointer (the pimpl-pointer or delegate) to a forward declared implementation class.

In your source, declare the implementation class, forward every public method of your public class to the delegate, and construct an instance of your pimpl class in every constructor of your public class.

Plus:

  • Allows you to change the implementation of your class without having to recompile everything.
  • Inheritance works well, only the syntax becomes a little different.

Minus:

  • Lots and lots of stupid method bodies to write to do the delegation.
  • Kind of awkward to debug since you have tons of delegates to step through.
  • An extra pointer in your base class, which might be an issue if you have lots of small objects.
三生一梦 2024-07-31 09:16:00

John Lakos 的大规模 C++ 软件设计是一本出色的书,解决了所涉及的挑战构建大型 C++ 项目。 问题和解决办法都是立足于现实的,当然对上述问题进行了详细的讨论。 强烈推荐。

John Lakos' Large Scale C++ Software Design is an excellent book that addresses the challenges involved in building large C++ projects. The problems and solutions are all grounded in reality, and certainly the above problem is discussed at length. Highly recommended.

二手情话 2024-07-31 09:16:00

使用继承:

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

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

另外:

  • 允许您更改类的实现,而无需重新编译所有内容。
  • 实施起来简单又万无一失。

缺点:

  • 定义公共基类的(公共)派生实例确实很尴尬,它应该继承公共基类的(私有)实现的一些方法。

Using inheritance:

In your header, declare the public methods as pure virtual methods and a factory.

In your source, derive an implementation class from your interface and implement it. In the implementation of the factory return an instance of the implementation.

Plus:

  • Allows you to change the implementation of your class without having to recompile everything.
  • Easy and foolproof to implement.

Minus:

  • Really awkward to define a (public) derived instance of the public base class which should inherit some of the methods of the (private) implementation of the public base.
凤舞天涯 2024-07-31 09:16:00

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

You can use a forward declaration for class A that is referred to by pointer in another class B. You can then include class's A header file in class B's implementation file rather than its header file. That way, changes you make to class A will not affect source files that include class B's header file. Any class that wants to access class A's members will have to include class A's header file.

紫罗兰の梦幻 2024-07-31 09:16:00

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

当我接触一些常见的头文件后,当我不希望编译器编译世界时,我通常所做的就是使用 makefile/脚本来仅编译我知道需要重新编译的文件。 例如,如果我向类添加非虚拟私有函数,则只需重新编译该类的 cpp 文件,即使其头文件已被一百个其他文件包含。 在我离开之前,我启动了一个干净的构建来重建世界。

Refactoring and use pimpl/handle-body idiom, use pure virtual interfaces to hide implementation detail seems to be the popular answer. One should consider compile time and developer productivity when designing large systems. But what if you're working on existing large C++ system with no unit test coverage? Refactoring is usually out of the question.

What I usually do when I don't want the compiler to compile the world after I touched some common header files is to have a makefile/script to compile only the files I know need recompiling. For example, if I'm adding a non-virtual private function to a class, only the class's cpp file needs to be recompiled even when its header file is included by a hundred other files. Before I leave for the day, I kick off a clean build to rebuild the world.

美男兮 2024-07-31 09:16:00

无。

我明白使用一个的意义,但我认为以下论点在许多情况下减轻了这一点:

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

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

None.

I see the point in using one, but I think the following arguments mitigate that point in many scenarios:

  1. Clarity comes first. If compromising clarity for runtime
    speed must be considered twice, what
    about compromising clarity for
    compile time speed?
  2. Private members shouldn't change so often.
  3. Usually, it doesn't take that long to rebuild everything.
  4. Faster tools will come in the future, so the compile speed problem will be automatically mitigated. Your code will not become automatically clearer.
  5. You should rebuild frequently anyway.
  6. Have you tried Incredibuild?

Of course in the end this is an economic decision. If the weight of "3" is important in your project and for some reason "6" cannot apply, then go ahead: you will win more from using these templates than you lose.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文