C++ header files is a rather mundane topic by most standards. Talking about header files is not as interesting as discussing complex search algorithms or debating design patterns . It’s not an academically stimulating subject to teach, so most CS programs do not emphasize header file design in their courses.
However, not having the correct header file design decisions can have significant ramifications on your project in terms of increases build times, compilation fiascos, code maintainability issues and plain information leakage. The larges your C++ project is, the more important this becomes.
Here’s a short description of the top 10 header file issues that can crop up if you’re not careful and how to avoid them.
C ++头文件是大多数标准相当平凡的话题。说到头文件是不是在讨论复杂的搜索算法或辩论的设计模式一样有趣。这不是一个学术课题刺激教,所以大部分CS程序不强调头文件在设计自己的课程。When the preprocessor sees a #include, it replaces the #include with the contents of the specified header. Using a include guard , you can prevent a header file being included multiple times during the compilation process. The most common way to define an include guard is as follows:
当预处理器会看到一个的#include,它用指定的头的内容的#include。使用包括后卫,可以防止一个头文件在编译过程中被包含多次。以限定包括防护装置的最常见的方式是如下:
You usually name your #include guard the same as the name of your header file.
There are two main problems that #include guards help solve.
1. It can help prevent danger circular references between header files which can cause weird compilation failures.
Consider the following example where main.cpp includes both Airbus.h and Boeing.h:
你通常命名include防范与您的头文件的名称。
有迹象表明,#包括警卫帮助解决两个主要问题。
1.它可以帮助防止头文件这可能会导致奇怪的编译错误的危险循环引用。
看看下面的例子,其中的main.cpp包括Airbus.h和Boeing.h:
Compiling the code above gives the following error:
1>c:\users\user\documents\visual studio 2015\projects\smartpointers\headerfiles\airbus.h(2): fatal error C1014: too many include files: depth = 1024
If you’re in a large project with hundreds of include files, it might take some digging to find it out. If you’re using VS2015, you’re in luck because there’s an option to show the include order : Right Click the Project -> Properties -> C/C++ -> Advanced -> Show Includes. If you turn this on, you’ll see the following in the output window:
如果你在一个大的项目是数以百计的包含文件,它可能需要一些挖掘找到它。如果你使用VS2015,你很幸运,因为有显示包括订单的选项:右键单击项目 - >属性 - > C / C ++ - >高级 - >显示包括。如果打开这个选项,你会看到在输出窗口中以下内容:
Looking at this you can easily tell that there’s a circular reference between Boeing.h and Airbus.h. Fortunately, include guards can help fix the problem. The revised piece of code is below.
看着这个你可以很容易地告诉大家,有Boeing.h和Airbus.h之间的循环引用。幸运的是,包括守卫可以帮助解决这个问题。代码修改后的一段如下。
2. In the absence of an include guard, a file will need to be processed multiple times and can cause significant build delays in large systems.
2.在没有包括护罩,一个文件将需要多次处理,并可能导致在大型系统显著生成延迟。
Recommendation: Always use an include guard as shown above to optimize build times and avoid weird build errors. If your compiler supports and optimized #pragma once as an include guard mechanism, you should use that because it is usually more performant and less error prone than using an explicit include guard. For example, a lot of our internal code uses the following convention for public header files. Notice that if we're on a MS compiler where _MSC_VER is defined, we'll use the #pragma directive which is supported and optimized by the compiler.
建议:请始终使用如上图所示,优化生成时间,避免怪异的生成错误的包括后卫。如果你的编译器支持和优化的#pragma一次作为一个include防范机制,你应该使用,因为它通常是更好的性能和更小的误差比使用显式俯卧包括后卫。例如,很多我们的内部代码使用公共头文件下列约定。请注意,如果我们在哪里_MSC_VER定义一个MS的编译器,我们将使用它支持和编译器优化#pragma指令。
Headers should define only the names that are part of the interface, not names used in its own implementation. However, a using directive at top level in a header file injects names into every file that includes the header.
This can cause multiple issues:
Recommendations:
1. Try to avoid putting any using namespace declarations in your header files. If you absolutely need some namespace objects to get your headers to compile, please use the fully qualified names (Eg. std::cout , std::string )in the header files.
1.尽量避免将任何使用空间声明在你的头文件。如果你绝对需要一些空间物品,让您的头编译,请在头文件中使用完全合格的名称(如:性病::法院,性病::字符串)。
2. If recommendation #1 above causes too much code clutter – restrict your “using namespace” usage to within the class or namespace defined in the header file. Another option is using scoped aliases in your header files as shown below.
2.如果建议1以上引起太多的代码混乱 - 限制你的“使用命名空间”的使用在头文件中定义的类或命名空间内。如下图所示另一种选择是在头文件中使用范围的别名。
I’ve seen multiple cases where a header file becomes a dumping ground for all miscellaneous functionality added at a late phase in the project. Recently. I came across a codebase that lumped a logging functionality and HTTP Get/Post API in a single header file. This fundamentally violates the concept of Single Responsibility Principle in a module. Even worse, when I first started reading the code, I thought it was some sort of logger specific to networking/http – but it turned out that it was just a general purpose file logger which happened to share some helper functions from the http library in the same module !!! There is no way I can pull out either the HTTP or FileLogger for use in another project without significant rework.
我见过多次情况下,一个头文件成为在项目的后期阶段添加的所有杂项功能的倾销地。 最近。我碰到,在一个头文件集中日志记录功能,和HTTP GET / POST API代码库。这从根本上违反了单一职责原则的模块中的概念。更糟的是,当我第一次开始阅读的代码,我认为这是某种特定的记录仪的联网/ HTTP - 但事实证明这只是刚巧从HTTP库共享一些辅助功能的通用文件记录器同一模块!!!没有办法,我可以拉出来的HTTP或FileLogger对于没有显著返工在另一个项目中使用。
Recommendation: Each header file, which basically provides an interface for your client software, should provide one clearly identifiable piece of functionality. (The same goes for your cpp files).
建议:每个头文件,它基本上提供了一个接口为您的客户端软件,应提供对一个清晰可辨的功能块。 (这同样适用于你的cpp文件)。
http://blog.csdn.net/sergeycao/article/details/52537865
A header file should have everything it needs to compile by itself , i.e., it should explicitly #include or forward declare the types/ structs it needs to compile. If a header file does not have everything it needs to compile but the program incorporating the header file compiles, it indicates that somehow the header file is getting what it needs because of an include order dependency. This typically happens because another header file gets included in the compile chain before this incompilable header file which provides the missing functionality. If the include order/build order dependency changes, then the whole program might break in unexpected ways. The C++ compiler is notorious for misleading error messages and it might not be easy to locate the error at that point.
头文件应该有它需要由它自己来编译的一切,即,应该明确#包含或转发声明类型/结构需要进行编译。如果一个头文件不具有它需要编译,但该方案结合了头文件编译一切,这表明在某种程度上头文件越来越它需要什么,因为包括订单的依赖。这通常是因为另一个头文件被包括在这个incompilable头文件,它提供缺失的功能之前,编译链。如果包括订单/生成顺序依赖的变化,那么整个程序可能以意想不到的方式打破。 C ++编译器是臭名昭著的误导性的错误消息,它可能不容易在这一点上查找错误。
Recommendation: Check your header filies by compiling them in isolation via a testMain.cpp that includes nothing but the header file under test. If it produces a compilation error, then something either needs to get included in the header file or forward declared. The process should be repeated for all header files in the project using a bottoms-up approach. This’ll help prevent random build break as the codebase grows larger and code blocks are moved around.
建议:通过经由包括什么,但测试的头文件testMain.cpp单独编译它们检查你的头filies。如果它产生的编译错误,那么什么或者需要得到包括在头文件或转发声明。这个过程应该重复使用自下而上的方法在项目中的所有头文件。这样做有助于防止随意突破构建的代码库变得更大和代码块左右移动。
MISTAKE 5.a : Including non-required header files in your header – for example, including files that only the .cpp file code needs .
错误#5.A:包含在头的非需要的头文件 - 例如,包括只有cpp文件代码需要的文件。
A common example of un-needed header files in your header file is
不需要的头文件在头文件中一个常见的例子是<cmath>和<algorithm>。
Recommendation: Do not bloat your header files with unnecessary #includes.
建议:不要包括不必要的#头文件会膨胀。
This is really important if you’re creating and distributing DLLs. Each DLL is packaged with a header file that acts as a public interface of the functionality provided by the DLL. So If you’re developing a Protocol Handler to send AMQP Network Traffic, you’d not want to expose what implementation engine you’re using underneath the scenes.
如果你正在创造和分配的DLL这是非常重要的。每个DLL打包与作为由该DLL所提供的功能性的公共界面的头文件。所以,如果你正在开发一个协议处理器发送AMQP网络流量,你不希望暴露你使用的场景下实现什么样的发动机。
Recommendation: Only expose functionality that the client of your library needs in a header file.
建议:只有公开该库的客户端头文件中需要的功能。
http://blog.csdn.net/sergeycao/article/details/52537865
Mistake # 6: Not explicitly including all STL headers required by your cpp code file.
错误#6:没有明确包括您的cpp代码文件所需的所有STL头。
The standard does not specify which STL header files will be included by which other STL headers. So if you forget to include STL headers explicitly required by your code, it may work because the dependency is brought in via some other header file you included. However, any change / removal of dependencies can break the build in unexpected ways.
该标准并不指定哪些STL的头文件会由其他STL头被包括在内。所以,如果你忘了,包括你的代码中明确要求STL头,它可以工作,因为依赖是通过您包括一些其他的头文件带来英寸然而,任何改变/去除相关性可以以意想不到的方式打破建设。
Recommendation: Always explicitly include the STL functionality used by your cpp files . For example, if you use
建议:始终明确包括你的cpp文件使用STL功能。例如,如果你使用<算法>,明确包含在你的cpp文件头。
http://blog.csdn.net/sergeycao/article/details/52537865
Mistake # 7: Not making judicious use of forward declarations in header files
错误#7:未作出明智地使用着的声明在头文件
Forward declaration is an interesting technique often employed in C++ used to
远期声明是在C经常采用一种有趣的技术++使用
The code above fails to compile with the following arcane errors :
1> Note: including file: c:\users\debh\documents\visual studio 2015\projects\smartpointers\forwarddeclaration\Aircraft.h
1>c:\users\debh\documents\visual studio 2015\projects\smartpointers\forwarddeclaration\aircraft.h(7): error C2143: syntax error: missing ';' before '*'
1>c:\users\debh\documents\visual studio 2015\projects\smartpointers\forwarddeclaration\aircraft.h(7): error C4430: missing type specifier – int assumed. Note: C++ does not support default-int
1>c:\users\debh\documents\visual studio 2015\projects\smartpointers\forwarddeclaration\aircraft.h(7): error C2238: unexpected token(s) preceding ';'
This is what happened:
The fix is easy: Just forward declare the class Airport in “Aircraft.h
该修补程序非常简单:只需向前声明类机场“Aircraft.h
Recommendation: If you have cyclic dependencies between header file objects or just using < 10% of header file functionality, consider using forward declarations.
建议:如果你有头文件的对象或只使用<10%的头文件功能之间循环依赖,可以考虑使用前向声明。
http://blog.csdn.net/sergeycao/article/details/52537865
This sometimes happens because people want to share a bunch of code between cpp files for maintainability reasons. This is a bad idea – it can confuse the programmer , some IDE navigational features and even some build engines. Also, if this is a public API, people expect to get a set of header files to use your DLL or LIB. Getting a cpp file , they might think that something went wrong in the packaging/installation of the product.
这有时是因为人们希望共享一堆cpp文件之间的代码可维护性的原因。这是一个坏主意 - 它可以混淆程序员,一些IDE导航功能,甚至一些构建引擎。此外,如果这是一个公开的API,人们都希望得到一组头文件使用您的DLL或LIB。获得一个CPP文件,他们可能会觉得出事了,在包装/产品安装。
Recommendation: Please put all shared code in an internal header file.
建议:请把所有的共享代码放在一个内部的头文件。
http://blog.csdn.net/sergeycao/article/details/52537865
Mistake # 9: Declaring functions shared between multiple cpp files in separate header files/ code files.
错误#9:声明在单独的头文件/代码文件的多个cpp文件共享功能。
When multiple files compile against a single function, the declaration for that function must be in a single header file. This allows maintainers to update the function declaration in a single place and detect any errors at compile time. This also makes it impossible to declare the function using the wrong parameter types, since there’s an authoritative declaration.
当多个文件编译对一个单一的功能,该功能的声明必须在一个头文件。这使得维护,更新在一个地方函数声明,并在编译时检测到任何错误。这也使得它无法使用错误的参数类型声明的功能,因为有一个权威的声明。
Consider the following bad example of multiple declaration followed by a correct one:
考虑多种声明其次是正确的下列糟糕的例子:
BAD:
Correct Way:
Recommendation: Shared functions between cpp files should be defined just once in a single header file.
建议:cpp文件之间共享的功能应该以一个头文件只是一次被定义。
http://blog.csdn.net/sergeycao/article/details/52537865
Using the precompiled headers can significantly speed up your build time. One of the ways to screw it up is to include your own header files into the precompiled header file (pch.h or stdafx.h) . If you do, anytime those header files change, it’ll trigger a re-build of your project. The ideal candidates for inclusion in precompiled header are large header files that you don’t expect to change and is used by many of your cpp files– like windows.h, STL headers and header only implementations like rapid json.
使用预编译头可以显著加速你的build时间。其中一个方法来搞砸了是你自己的头文件包括到预编译头文件(pch.h或stdafx.h中)。如果你这样做,随时随地那些头文件改变,它会引发你的项目的重新构建。包含在预编译头的理想人选是,你不要指望改变,被许多您的cpp文件 - 像WINDOWS.H,STL头和头只能实现比如快速JSON的大型头文件。
Recommendation: Put only headers that’ll not change in your precompiled headers.
建议:只放头文件而不是改变你的预编译头文件。