RWYQ阿伟 | 2025-02-05 | 笔记 | 0条留言 | 7 | 2025-02-05更新
In general, every .cc
file should have an associated .h
file. There are some common exceptions, such as unit tests and small .cc
files containing just a main()
function.
Correct use of header files can make a huge difference to the readability, size and performance of your code.
The following rules will guide you through the various pitfalls of using header files.
Header files should be self-contained (compile on their own) and end in .h
. Non-header files that are meant for inclusion should end in .inc
and be used sparingly.
All header files should be self-contained. Users and refactoring tools should not have to adhere to special conditions to include the header. Specifically, a header should have header guards and include all other headers it needs.
When a header declares inline functions or templates that clients of the header will instantiate, the inline functions and templates must also have definitions in the header, either directly or in files it includes. Do not move these definitions to separately included header (-inl.h
) files; this practice was common in the past, but is no longer allowed. When all instantiations of a template occur in one .cc
file, either because they're explicit or because the definition is accessible to only the .cc
file, the template definition can be kept in that file.
There are rare cases where a file designed to be included is not self-contained. These are typically intended to be included at unusual locations, such as the middle of another file. They might not use header guards, and might not include their prerequisites. Name such files with the .inc
extension. Use sparingly, and prefer self-contained headers when possible.
All header files should have #define
guards to prevent multiple inclusion. The format of the symbol name should be <PROJECT>_<PATH>_<FILE>_H_
.
To guarantee uniqueness, they should be based on the full path in a project's source tree. For example, the file foo/src/bar/baz.h
in project foo
should have the following guard:
#ifndef FOO_BAR_BAZ_H_ #define FOO_BAR_BAZ_H_ ... #endif // FOO_BAR_BAZ_H_
If a source or header file refers to a symbol defined elsewhere, the file should directly include a header file which properly intends to provide a declaration or definition of that symbol. It should not include header files for any other reason.
Do not rely on transitive inclusions. This allows people to remove no-longer-needed #include
statements from their headers without breaking clients. This also applies to related headers - foo.cc
should include bar.h
if it uses a symbol from it even if foo.h
includes bar.h
.
Avoid using forward declarations where possible. Instead, include the headers you need.
A "forward declaration" is a declaration of an entity without an associated definition.
// In a C++ source file: class B; void FuncInB(); extern int variable_in_b; ABSL_DECLARE_FLAG(flag_in_b);
Forward declarations can save compile time, as #include
s force the compiler to open more files and process more input.
Forward declarations can save on unnecessary recompilation. #include
s can force your code to be recompiled more often, due to unrelated changes in the header.
Forward declarations can hide a dependency, allowing user code to skip necessary recompilation when headers change.
A forward declaration as opposed to an #include
statement makes it difficult for automatic tooling to discover the module defining the symbol.
A forward declaration may be broken by subsequent changes to the library. Forward declarations of functions and templates can prevent the header owners from making otherwise-compatible changes to their APIs, such as widening a parameter type, adding a template parameter with a default value, or migrating to a new namespace.
Forward declaring symbols from namespace std::
yields undefined behavior.
It can be difficult to determine whether a forward declaration or a full #include
is needed. Replacing an #include
with a forward declaration can silently change the meaning of code:
// b.h: struct B {}; struct D : B {}; // good_user.cc: #include "b.h" void f(B*); void f(void*); void test(D* x) { f(x); } // Calls f(B*)
If the #include
was replaced with forward decls for B
and D
, test()
would call f(void*)
.
Forward declaring multiple symbols from a header can be more verbose than simply #include
ing the header.
Structuring code to enable forward declarations (e.g., using pointer members instead of object members) can make the code slower and more complex.
Try to avoid forward declarations of entities defined in another project.
Define functions inline only when they are small, say, 10 lines or fewer.
You can declare functions in a way that allows the compiler to expand them inline rather than calling them through the usual function call mechanism.
Inlining a function can generate more efficient object code, as long as the inlined function is small. Feel free to inline accessors and mutators, and other short, performance-critical functions.
Overuse of inlining can actually make programs slower. Depending on a function's size, inlining it can cause the code size to increase or decrease. Inlining a very small accessor function will usually decrease code size while inlining a very large function can dramatically increase code size. On modern processors smaller code usually runs faster due to better use of the instruction cache.
A decent rule of thumb is to not inline a function if it is more than 10 lines long. Beware of destructors, which are often longer than they appear because of implicit member- and base-destructor calls!
Another useful rule of thumb: it's typically not cost effective to inline functions with loops or switch statements (unless, in the common case, the loop or switch statement is never executed).
It is important to know that functions are not always inlined even if they are declared as such; for example, virtual and recursive functions are not normally inlined. Usually recursive functions should not be inline. The main reason for making a virtual function inline is to place its definition in the class, either for convenience or to document its behavior, e.g., for accessors and mutators.
Include headers in the following order: Related header, C system headers, C++ standard library headers, other libraries' headers, your project's headers.
All of a project's header files should be listed as descendants of the project's source directory without use of UNIX directory aliases .
(the current directory) or ..
(the parent directory). For example, google-awesome-project/src/base/logging.h
should be included as:
#include "base/logging.h"
Headers should only be included using an angle-bracketed path if the library requires you to do so. In particular, the following headers require angle brackets:
C and C++ standard library headers (e.g. <stdlib.h>
and <string>
).
POSIX, Linux, and Windows system headers (e.g. <unistd.h>
and <windows.h>
).
In rare cases, third_party libraries (e.g. <Python.h>
).
In dir/foo.cc
or dir/foo_test.cc
, whose main purpose is to implement or test the stuff in dir2/foo2.h
, order your includes as follows:
dir2/foo2.h
.
A blank line
C system headers, and any other headers in angle brackets with the .h
extension, e.g., <unistd.h>
, <stdlib.h>
, <Python.h>
.
A blank line
C++ standard library headers (without file extension), e.g., <algorithm>
, <cstddef>
.
A blank line
Other libraries' .h
files.
A blank line
Your project's .h
files.
Separate each non-empty group with one blank line.
With the preferred ordering, if the related header dir2/foo2.h
omits any necessary includes, the build of dir/foo.cc
or dir/foo_test.cc
will break. Thus, this rule ensures that build breaks show up first for the people working on these files, not for innocent people in other packages.
dir/foo.cc
and dir2/foo2.h
are usually in the same directory (e.g., base/basictypes_test.cc
and base/basictypes.h
), but may sometimes be in different directories too.
Note that the C headers such as stddef.h
are essentially interchangeable with their C++ counterparts (cstddef
). Either style is acceptable, but prefer consistency with existing code.
Within each section the includes should be ordered alphabetically. Note that older code might not conform to this rule and should be fixed when convenient.
For example, the includes in google-awesome-project/src/foo/internal/fooserver.cc
might look like this:
#include "foo/server/fooserver.h" #include <sys/types.h> #include <unistd.h> #include <string> #include <vector> #include "base/basictypes.h" #include "foo/server/bar.h" #include "third_party/absl/flags/flag.h"
Exception:
Sometimes, system-specific code needs conditional includes. Such code can put conditional includes after other includes. Of course, keep your system-specific code small and localized. Example:
#include "foo/public/fooserver.h" #include "base/port.h" // For LANG_CXX11. #ifdef LANG_CXX11 #include <initializer_list> #endif // LANG_CXX11
博客内容遵循 署名-非商业性使用-相同方式共享4.0国际(CC BY-NC-SA 4.0)协议。
本文链接:https://rwyqboy.top/post/1892.html
版权声明:本文由阿伟的笔记本发布,如需转载请注明出处。
◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。
标题:Header Files
作者:RWYQ阿伟
浏览:7
发布:2025-02-05
更新:2025-02-05
分类:笔记
标签: 笔记 C++ 编程