被数千行代码淹没?
目录中的每个类都有一组头文件/源文件似乎有点过头了。如果课程数量接近 100 或 1000,甚至可能会令人恐惧。
但是根据“让我们把所有东西放在一起”的理念与消息来源一起玩,结论是只有写文件的人才有希望不迷失在里面。即使使用 IDE,也很容易遗漏一些事情,因为当您使用 20,000 行代码的源代码时,您只会对任何与您的问题不完全相关的事情闭上思绪。
现实生活中的例子:那千行源代码中定义的类层次结构将自身封闭为菱形继承,并且子类中的某些方法被具有完全相同代码的方法覆盖。这很容易被忽略(谁想探索/检查 20,000 行的源代码?),并且当更改原始方法(错误更正)时,效果不如异常普遍。
依赖变得循环?
我在使用模板代码时遇到了这个问题,但我在常规 C++ 和 C 代码中看到了类似的问题。
将您的源代码分解为每个结构/类的 1 个标头可以让您:
- 加速编译,因为您可以使用符号前向声明而不是包含整个对象
- 类之间有循环依赖(§)(即类A有一个指向B的指针,B有一个指向A的指针)
在源代码控制的代码中,类依赖关系可能导致类在文件中上下移动,只是为了使头文件能够编译。在比较不同版本中的相同文件时,您不想研究此类移动的演变。
具有单独的标头使代码更加模块化,编译速度更快,并且更容易通过不同版本差异研究其演变
对于我的模板程序,我必须将头文件分成两个文件:包含模板类声明/定义的 .HPP 文件和包含上述类方法定义的 .INL 文件。
将所有这些代码放在一个且只有一个唯一的标头中意味着将类定义放在该文件的开头,将方法定义放在末尾。
然后,如果有人只需要一小部分代码,使用只有一个标头的解决方案,他们仍然需要为较慢的编译付费。
(§) 请注意,如果您知道哪个类拥有哪个类,则可以在类之间建立循环依赖关系。这是关于知道其他类存在的类的讨论,而不是 shared_ptr 循环依赖反模式。
最后一句话:标题应该是自给自足的
但是,必须通过多个标题和多个来源的解决方案来尊重一件事。
当您包含一个标头时,无论是哪个标头,您的源代码都必须干净地编译。
每个标题都应该是自给自足的。您应该开发代码,而不是通过 grep 您的 10,000 多个源文件项目来查找哪个标头定义了您需要包含的 1,000 行标头中的符号,只是因为一个枚举。
这意味着每个标头要么定义或前向声明它使用的所有符号,要么包括所有需要的标头(并且仅包含所需的标头)。
关于循环依赖的问题
下划线-d问:
你能解释一下使用单独的头文件对循环依赖有什么影响吗?我认为不会。即使两个类都在同一个标头中完全声明,我们也可以轻松地创建循环依赖,只需在我们在另一个中声明它的句柄之前提前声明一个。其他一切似乎都很好,但是单独的标头促进循环依赖的想法似乎很遥远
underscore_d,11 月 13 日 23:20
假设您有 2 个类模板,A 和 B。
假设类 A (resp. B) 的定义有一个指向 B (resp. A) 的指针。假设 A 类(对应 B)的方法实际上调用了 B(对应 A)的方法。
在类的定义和它们的方法的实现中都有循环依赖。
如果 A 和 B 是普通类,并且 A 和 B 的方法在 .CPP 文件中,则没有问题:您将使用前向声明,每个类定义都有一个标头,那么每个 CPP 将包含两个 HPP。
但是因为你有模板,你实际上必须重现上面的模式,但只有标题。
这表示:
- 定义头 A.def.hpp 和 B.def.hpp
- 实现头 A.inl.hpp 和 B.inl.hpp
- 为方便起见,“天真”标题 A.hpp 和 B.hpp
每个标头将具有以下特征:
- 在 A.def.hpp (resp. B.def.hpp) 中,您有一个类 B (resp. A) 的前向声明,这将使您能够声明对该类的指针/引用
- A.inl.hpp (resp. B.inl.hpp) 将包括 A.def.hpp 和 B.def.hpp,这将使来自 A (resp. B) 的方法能够使用类 B (resp. A) .
- A.hpp (resp. B.hpp) 将直接包含 A.def.hpp 和 A.inl.hpp (resp. B.def.hpp 和 B.inl.hpp)
- 当然,所有标头都需要自给自足,并受标头守卫保护
天真的用户将包括 A.hpp 和/或 B.hpp,从而忽略整个混乱。
拥有这种组织意味着库编写者可以解决 A 和 B 之间的循环依赖关系,同时将两个类保存在单独的文件中,一旦您理解了该方案,就可以轻松导航。
请注意,这是一个边缘案例(两个模板相互认识)。我希望大多数代码不需要那个技巧。