大型 C++ 项目中的一个问题可能是构建时间。在你的依赖树中有一些你需要处理的类,但通常你会避免这样做,因为每次构建都需要很长时间。您不一定要更改其公共接口,但也许您想更改其私有成员(添加缓存变量,提取私有方法,...)。您面临的问题是,在 C++ 中,甚至私有成员都在公共头文件中声明,因此您的构建系统需要重新编译所有内容。
在这种情况下你会怎么做?
我已经勾勒出两种我知道的解决方案,但它们都有自己的缺点,也许还有一个更好的我还没有想到的解决方案。
大型 C++ 项目中的一个问题可能是构建时间。在你的依赖树中有一些你需要处理的类,但通常你会避免这样做,因为每次构建都需要很长时间。您不一定要更改其公共接口,但也许您想更改其私有成员(添加缓存变量,提取私有方法,...)。您面临的问题是,在 C++ 中,甚至私有成员都在公共头文件中声明,因此您的构建系统需要重新编译所有内容。
在这种情况下你会怎么做?
我已经勾勒出两种我知道的解决方案,但它们都有自己的缺点,也许还有一个更好的我还没有想到的解决方案。
粉刺模式:
在您的头文件中,仅将公共方法和私有指针(pimpl 指针或委托)声明到前向声明的实现类。
在您的源代码中,声明实现类,将公共类的每个公共方法转发给委托,并在公共类的每个构造函数中构造您的 pimpl 类的实例。
加:
减:
John Lakos 的《大规模 C++ 软件设计》是一本出色的书,它解决了构建大型 C++ 项目所涉及的挑战。问题和解决方案都是立足于现实的,当然,上面的问题也得到了详细的讨论。强烈推荐。
使用继承:
在您的标头中,将公共方法声明为纯虚拟方法和工厂。
在您的源代码中,从您的接口派生一个实现类并实现它。在工厂的实现中返回一个实现的实例。
加:
减:
您可以对由另一个类 B 中的指针引用的类 A 使用前向声明。然后您可以将类的 A 头文件包含在类 B 的实现文件中,而不是其头文件中。这样,您对 A 类所做的更改不会影响包含 B 类头文件的源文件。任何想要访问 A 类成员的类都必须包含 A 类的头文件。
重构和使用 pimpl/handle-body 成语,使用纯虚拟接口隐藏实现细节似乎是流行的答案。在设计大型系统时,应该考虑编译时间和开发人员的生产力。但是,如果您正在使用没有单元测试覆盖率的现有大型 C++ 系统怎么办?重构通常是不可能的。
在我接触了一些常见的头文件之后,当我不想让编译器编译世界时,我通常会做一个 makefile/script 来只编译我知道需要重新编译的文件。例如,如果我正在向一个类添加一个非虚拟私有函数,那么即使它的头文件包含在一百个其他文件中,也只需要重新编译该类的 cpp 文件。在我离开这一天之前,我开始了一个干净的构建来重建世界。
没有任何。
我看到使用一个的意义,但我认为以下论点在许多情况下减轻了这一点:
当然,这最终是一个经济决定。如果“3”的权重在您的项目中很重要,并且由于某种原因“6”不能适用,那么请继续:使用这些模板您将获得比您失去的更多的收益。