有关如何正确使用 #include
的材料吗?
我没有找到任何详细解释此用法的 C/C++ 教材。
在正式项目中,我总是在处理它时感到困惑。
让我经常被卡住的是这个:
这将在头文件路径中进行搜索:
#include <stdio.h>
这将在您的本地目录中搜索:
#include "myfile.h"
对于每个头文件,你应该做的第二件事情是:
myfilename.h:
#ifndef MYFILENAME_H
#define MYFILENAME_H
//put code here
#endif
这个模式意味着在重新定义编译中的标头时,您不能失误(感谢orsogufo指出这被称为“包含保护”)。请阅读一些关于C编译器如何编译文件(在链接之前)的文章,因为这将使#define和#include的世界对您来说更加清晰。当涉及解析文本时,C编译器本身并不是非常聪明。(然而,当涉及到C编译器本身时,又是另一回事了)
基本上你需要了解在哪些情况下不需要包含头文件,例如前向声明。另外要确保每个包含文件都可以单独编译,并且只有在必要时(例如模板)才将#includes放在h文件中。
因此,您的编译器可能支持2个用于包含文件的唯一搜索路径:
非正式地,我们可以称之为“系统包含路径”和“用户包含路径”。
#include <XX> 搜索系统包含路径。
#include "XX" 首先搜索用户包含路径,然后是系统包含路径。
检查草案标准 n2521:
第16.2节:
2 A preprocessing directive of the form
# include < h-char-sequence> new-line
searches a sequence of implementation-defined places for a header identified
uniquely by the specified sequence between the < and > delimiters, and
causes the replacement of that directive by the entire contents of the
header. How the places are specified or the header identified is
implementation-defined.
3 A preprocessing directive of the form
# include " q-char-sequence" new-line
causes the replacement of that directive by the entire contents of the
source file identified by the specified sequence between the " " delimiters.
The named source file is searched for in an implementation-defined manner.
If this search is not supported, or if the search fails, the directive is
reprocessed as if it read
# include < h-char-sequence> new-line
with the identical contained sequence (including > characters, if any)
from the original directive.
这的一个例子是gcc
-isystem <dir> Add <dir> to the start of the system include path
-idirafter <dir> Add <dir> to the end of the system include path
-iwithprefix <dir> Add <dir> to the end of the system include path
-iquote <dir> Add <dir> to the end of the quote include path
-iwithprefixbefore <dir> Add <dir> to the end of the main include path
-I <dir> Add <dir> to the end of the main include path
要查看gcc搜索的位置,请执行以下操作:
g++ -v -E -xc++ /dev/null -I LOOK_IN_HERE
#include "..." search starts here:
#include <...> search starts here:
LOOK_IN_HERE
/usr/include/c++/4.0.0
/usr/include/c++/4.0.0/i686-apple-darwin9
/usr/include/c++/4.0.0/backward
/usr/local/include
/usr/lib/gcc/i686-apple-darwin9/4.0.1/include
/usr/include
/System/Library/Frameworks (framework directory)
/Library/Frameworks (framework directory)
End of search list.
那么如何使用这个知识呢?
有几种思路,但我总是将我的库从最具体的到最通用的列出。
示例
文件:plop.cpp
#include "plop.h"
#include "plop-used-class.h"
/// C Header Files
#include <stdio.h> // I know bad example but I drew a blank
/// C++ Header files
#include <vector>
#include <memory>
这样如果头文件“plop-used-class.h”应该包含<vector>,则编译器将捕获到此错误。如果我将<vector>放在顶部,则这个错误将被隐藏,不会被编译器捕获。
#include "Y.h"
class X
{
Y y; // need header for Y
};
无需标题:
class Y;
class X
{
Y* y; // don't need header for Y
};
//#include "Y.h" in .cpp file
作为对Andy Brice答案的补充,你也可以通过函数返回值的前向声明来解决问题:
class Question;
class Answer;
class UniversityChallenge
{
...
Answer AskQuestion( Question* );
...
};
这里有一个我之前提出的问题,其中有一些很好的答案,链接如下http://bytes.com/groups/c/606466-forward-declaration-allowed.
#include
是将多个文件组合成 C++ 翻译单元的唯一实用方式。这是一个简单、有限的工具,通过预处理器将整个文件复制/粘贴到其他文件中。结果编译器的输入通常很大,并且常常从一个翻译单元重复到另一个翻译单元。编辑:安迪·布赖斯以更简洁的方式提出了同样的观点。
接上null的回答,最重要的是考虑你放置 #include 的位置。
当你写一个 #include 时,预处理器会将你在当前文件中列出的文件内容直接包含进来,包括这些文件中的任何 #include。这显然会导致在编译时文件变得非常大(代码膨胀),所以你需要仔细考虑是否需要 #include。
在标准的代码文件布局中,你为一个类设置一个 .h 文件用于存放类和函数的声明,然后有一个 .cpp 实现文件。在头文件中放置 #include 的数量时要小心。因为每当你对头文件进行更改时,也必须重新编译任何包含它的文件(即使用该类的文件);如果头文件本身有很多 #include,那么使用该类的每个文件在编译时都会显著增大。
最好在可能的情况下使用前向声明,这样你可以编写方法签名等,并在 .cpp 文件中 #include 相关文件,以便你可以实际使用代码所依赖的类和结构。
//In myclass.h
class UtilClass; //Forward declaration of UtilClass - avoids having to #include untilclass.h here
class MyClass
{
MyClass();
~MyClass();
void DoSomethingWithUtils(UtilClass *util); //This will compile due to forward declaration above
};
//Then in the .cpp
#include utilclass.h
void MyClass::DoSomethingWithUtils(UtilClass *util)
{
util->DoSomething(); //This will compile, because the class definition is included locally in this .cpp file.
}
#include utilclass.h
处停止;所以最好在真正尝试编译之前先检查一下... - Wolf
#pragma once
的等效写法。 - xtofl