您的位置:首页 > 其它

C ++头文件的十大错误,如何解决这些问题

2016-09-14 15:03 344 查看


Top 10 C++ header file mistakes and how to fix them

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程序不强调头文件在设计自己的课程。

但是,没有正确的头文件中的设计决策会对您的项目显著后果在增加方面构建时间,编译惨败,代码的可维护性问题和平原信息泄露。在拉尔您的C ++项目,更重要的这成为。

下面是如果你不小心,以及如何避免它们能突然出现在排名前10位的头文件问题的简短说明。
http://blog.csdn.net/sergeycao/article/details/52537865


Mistake
# 1: Not using “include guards” in a header file. 错误#1:不使用“包控制”在头文件。

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。使用包括后卫,可以防止一个头文件在编译过程中被包含多次。以限定包括防护装置的最常见的方式是如下:

//File: Aircraft.h
#ifndef AIRCRAFT_H
#define AIRCRAFT_H

\\the entire file

#endif

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:

//File: Airbus.h
#include "Boeing.h"
namespace Airbus
{
class Carrier
{
Carrier();
~Carrier();
};
}

//File: Boeing.h
#include "Airbus.h"
namespace Boeing
{
class Carrier
{
Carrier();
~Carrier();
};
}

// main.cpp : Defines the entry point for the console application.
#include "stdafx.h"
#include "Boeing.h"
#include "Airbus.h"

int main()
{
return 0;
}

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之间的循环引用。幸运的是,包括守卫可以帮助解决这个问题。代码修改后的一段如下。

//File: Airbus.h
#ifndef AIRBUS_H
#define AIRBUS_H

#include "Boeing.h"

namespace Airbus
{
class Carrier
{
Carrier();
~Carrier();
};
}
#endif

//File: Boeing.h
#ifndef BOEING_H
#define BOEING_H

#include "Airbus.h"
namespace Boeing
{
class Carrier
{
Carrier();
~Carrier();
};
}
#endif

// main.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include "Boeing.h"
#include "Airbus.h"

int main()
{
return 0;

}

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指令。

#ifndef HEADER_FILE
#define HEADER_FILE

#ifdef _MSC_VER
#pragma once
#endif  // _MSC_VER

// Contents of the header file here

#endif  // HEADER_FILE

http://blog.csdn.net/sergeycao/article/details/52537865


MISTAKE
# 2: Incorporating "using namespace" statements at top level in a header file

错误#2:在头文件顶层纳入“使用命名空间”语句

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:

It
is not possible for a consumer of your header file to undo the namespace include – thus they are forced to live with your namespace using decision, which is undesirable.

It
dramatically increases the chance of naming collissions that namespaces were meant to solve in the first place.

It
is possible that a working version of the program will fail to compile when a new version of the library is introduced. This happens if the new version introduces a name that conflicts with a name that the application is using from another library.

The
“using namespace” part of the code takes effect from the point where it appears in the code that included your header, meaning that any code appearing before that might get treated differently from any code appearing after that point.

头应该定义只有那些接口,在它自己的实现不使用名称的一部分的名字。然而,使用指令在头文件中顶级的名字注入到每个包含头文件。

这可能会导致多种问题:

这是不可能的头文件的消费者撤消命名空间包含 - 因此,他们被迫生活与你的空间使用的决定,这是不可取的。

这大大增加了命名命名空间注定摆在首位,以解决冲突的机会。

可能的是该程序的工作版本将失败时被引入该库的新版本进行编译。如果发生这种情况,新版本引入了与该应用程序是从另一个库使用名称相冲突的名字。

代码中的“使用命名空间”的一部分生效了点地方出现,包括你的头,这意味着在此之前出现的任何代码可能会被从该点后出现的任何代码区别对待的代码。

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.尽量避免将任何使用空间声明在你的头文件。如果你绝对需要一些空间物品,让您的头编译,请在头文件中使用完全合格的名称(如:性病::法院,性病::字符串)。

//File:MyHeader.h:
class MyClass
{
private:
Microsoft::WRL::ComPtr<iunknown> _parent;
Microsoft::WRL::ComPtr<iunknown> _child;
}
</iunknown></iunknown>

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以上引起太多的代码混乱
- 限制你的“使用命名空间”的使用在头文件中定义的类或命名空间内。如下图所示另一种选择是在头文件中使用范围的别名。

//File:MyHeader.h:

class MyClass
{
namespace wrl = Microsoft::WRL; // note the aliasing here !
private:
wrl::ComPtr<iunknown> _parent;
wrl::ComPtr<iunknown> _child;
}
</iunknown></iunknown>

http://blog.csdn.net/sergeycao/article/details/52537865


MISTAKE
# 3 : Having multiple unrelated functionality grouped together in a single header file (and cpp file)

错误#3:具有一个头文件(和cpp文件)组合在一起的多个不相关的功能

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


MISTAKE
# 4: Not making the header file compliable by itself

错误#4:本身不使头文件编译

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。如果它产生的编译错误,那么什么或者需要得到包括在头文件或转发声明。这个过程应该重复使用自下而上的方法在项目中的所有头文件。这样做有助于防止随意突破构建的代码库变得更大和代码块左右移动。 http://blog.csdn.net/sergeycao/article/details/52537865
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> and <algorithm>.

不需要的头文件在头文件中一个常见的例子是<cmath>和<algorithm>。

Recommendation: Do
not bloat your header files with unnecessary #includes.

建议:不要包括不必要的#头文件会膨胀。


Mistake
# 5.b : Putting too much information in a header file and causing information leakage.

错误#5.b 把一个头文件中太多的信息和使用信息的泄漏。

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 <algorithms>, include that header explicitly in your cpp file.

建议:始终明确包括你的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经常采用一种有趣的技术++使用

Reduce
compile times: If your header needs a type declared in another header to compile, you have two options : either include the dependent header in your header file or forward declare the types in your header file. If the dependent header file is very large
and you only need to use say 5% of the types in the dependent header, it’s much better to use forward declaration to make those types known in your header file than to bring in the full dependent header. If your header file is included by multiple projects
in a very large solution , it can shave off hours from the build time.

Break
cyclic dependency between code: Imagine a situation where you have an Aircraft class and an Airport class. An Aircraft has reference to an Airport as it’s home base and an Airport has a fleet of Aircrafts. In this situation , the Aircraft class needs
to know the declaration of Airport exists and vice-versa. If you make both header file include each other, we’ll end up in a never ending cyclic dependency. Consider the followiing piece of code:

减少编译时间:如果你的头需要另一头编译声明的类型,你有两个选择:要么包括相关的头在你的头文件或向前你的头文件中声明的类型。如果相关的头文件是非常大的,你只需要使用说,在相关的报头的类型,5%,这是最好使用前置声明,使你的头文件已知的类型,而不是在全依赖的头带上。如果你的头文件是在一个非常大的解决方案包括多个项目,可以从制作的时候刮掉小时。

打破循环依赖代码之间:想象一下,你有一个飞机类和类机场的情况。飞机具有一定的参考到机场,因为它是基地和机场有飞机组成的机队。在这种情况下,飞机类需要知道声明的机场存在,反之亦然。如果使这两个头文件互相包含,我们将在一个永无止境的循环依赖结束。考虑followiing一段代码:

#pragma once
//File: Aircraft.h
#include "Airport.h"

class Aircraft
{
Airport* m_HomeBase;
};

#pragma once
//File: Airport.h
#include <vector>
#include "Aircraft.h"

class Airport
{
std::vector<aircraft> m_Fleet;
};

// ForwardDeclaration.cpp : Defines the entry point for the console application.

#include "stdafx.h"
#include "Airport.h"

int main()
{
return 0;
}
</aircraft></vector>

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:

Main
included “Airport.h”

The
first thing “Airport.h” included is “Aircraft.h”

While
trying to include “Aircraft.h”, the compiler does not know a definition of “Airport” which is used in “Aircraft.h” header. At this point, it fails compilation.

事情是这样的:

主要包括“Airport.h”

的第一件事“Airport.h”收录为“Aircraft.h”

虽然试图包括“Aircraft.h”,编译器不知道这是在“Aircraft.h”头使用的“机场”的定义。在这一点上,它失败编译。

The
fix is easy: Just forward declare the class Airport in “Aircraft.h

该修补程序非常简单:只需向前声明类机场“Aircraft.h

#pragma once
//File: Aircraft.h
#include "Airport.h"

class Airport; //Forward Declare Airport!

class Aircraft
{
Airport* m_HomeBase;
};

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
曹纪乾曹浩洋的专栏


Mistake
# 8: Including a cpp file in a header file.

错误#8:包括一个头文件的cpp文件。

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:

lib\Square.h
int Square(int a);
lib\Square.cpp
int Square(int a) { return a*a; }
myProgram\main.cpp
int Square(int a);           // declare Square() a second time - Bad !!!
void DoStuff() { Square(33); }    // use Square()

Correct
Way:


lib\Square.h
int Square(int a);
lib\Square.cpp
int Square(int a) { return a*a; }
myProgram\main.cpp
#include <square.h>
void DoStuff() { Square(33); }    // use Square()
</square.h>

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
曹纪乾曹浩洋的专栏


Mistake
# 10: Putting your project’s header files into the precompiled header file.

错误#10:把你的项目的头文件放在预编译头文件中。

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.

建议:只放头文件而不是改变你的预编译头文件。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐