在处理大型C/C++项目时,您是否对源文件或头文件中的#include有一些特定规则?
例如,我们可以想象遵循这两个过多的规则之一:
.h文件中禁止使用#include ; 每个.c文件都包含它需要的所有头文件
每个.h文件应包含其所有依赖项,即它应该能够单独编译而不会出现任何错误.
我想在任何项目之间存在权衡,但你的是什么?你有更具体的规则吗?或任何解决任何解决方案的链接?
做.h仅包含C文件意味着如果我只包含一个标题(定义我想在C文件中使用的东西)可能会失败.它可能会失败,因为我必须预先包含20个其他标题.更糟糕的是,我必须按正确的顺序包括它们.有了很多.h文件,从长远来看,这个系统最终会成为一个管理地狱.您只想在一个.c文件中包含一个.h文件,并花费2个小时来查找您需要的其他.h文件以及必须包含它们的顺序.
如果一个.h文件需要另一个.h文件成功包含在一个C文件中,除了这个.h文件之外不包含任何其他东西而且不会导致编译错误,我会在.h文件中包含这个.h文件.这样我就确定每个C文件都可以包含每个.h文件,它永远不会导致错误..c文件永远不必担心要导入哪些其他.h文件或以哪种顺序包含它们.这甚至适用于大型项目(1000 .h文件及以上).
另一方面,如果没有必要,我永远不会将.h文件包含在另一个文件中.例如,如果我有hashtable.h和hashtable.c,并且hashtable.c需要hashing.h,但hashtable.h不需要它,我不在那里包含它.我只将它包含在.c文件中,因为包括hashtable.h在内的其他.c文件也不需要hashing.h,如果他们因任何原因需要它,它们应该包含它.
我认为两条建议的规则都很糟糕.就我而言,我总是申请:
仅包含仅使用此标头中定义的文件编译文件所需的头文件.这意味着:
作为引用或指针出现的所有对象都应该是前向声明的
包括定义标题本身中使用的函数或对象的所有标题.
我会使用规则2:
所有标题都应该是自给自足的,无论如何:
不使用别处定义的任何东西
转发声明其他地方定义的符号
包括定义无法向前声明的符号的标题.
因此,如果您有一个空的C/C++源文件,包括一个标题应该正确编译.
然后,在C/C++源文件中,仅包含必要的内容:如果HeaderA正向声明了在HeaderB中定义的符号,并且您使用此符号,则必须同时包含两个...好消息是,如果您不要使用前向声明的符号,那么你将只能包含HeaderA,并避免包含HeaderB.
请注意,使用模板进行验证会使此验证"包括您的标题应该编译的空源"更复杂(并且有趣......)
只要存在循环依赖关系,第一条规则就会失败.所以它不能严格应用.
(这仍然可以工作,但这会将大量工作从程序员转移到这些库的消费者,这显然是错误的.)
我全都支持规则2(虽然包含"前向声明头"而不是真正的交易可能是好的,
因为这会减少编译时间).一般来说,我相信这是一种自我文档,如果一个头文件"声明"它有什么依赖 - 并且有什么更好的方法来做到这一点,而不是包含所需的文件?
在评论中,我一直受到挑战,标题之间的循环依赖性是设计不良的标志,应该避免.
那不对.实际上,类之间的循环依赖可能是不可避免的,并且根本不是设计糟糕的标志.例子很丰富,我只想提一下Observer模式,它在观察者和主体之间有一个循环引用.
要解决类之间的循环,必须使用前向声明,因为声明的顺序在C++中很重要.现在,以循环方式处理此前向声明以减少整个文件的数量并集中代码是完全可以接受的.无可否认,以下情况不符合这种情况,因为只有一个前向声明.但是,我已经在一个图书馆工作了.
// observer.hpp class Observer; // Forward declaration. #ifndef MYLIB_OBSERVER_HPP #define MYLIB_OBSERVER_HPP #include "subject.hpp" struct Observer { virtual ~Observer() = 0; virtual void Update(Subject* subject) = 0; }; #endif
// subject.hpp #includestruct Subject; // Forward declaration. #ifndef MYLIB_SUBJECT_HPP #define MYLIB_SUBJECT_HPP #include "observer.hpp" struct Subject { virtual ~Subject() = 0; void Attach(Observer* observer); void Detach(Observer* observer); void Notify(); private: std::list
m_Observers; }; #endif