如何正确使用 #include 指令?

How to use #include directive correctly?

提问人:MainID 提问时间:1/21/2009 最后编辑:undur_gongorMainID 更新时间:7/10/2023 访问量:24759

问:

有没有关于如何正确使用的材料? 我没有找到任何详细解释这种用法的 C/C++ 教科书。 在正式的项目中,我总是在处理它时感到困惑。#include

C++ C

评论


答:

26赞 Spence 1/21/2009 #1

总是让我绊倒的大问题是:

这将在标头路径中搜索:

#include <stdio.h>

这会在您的本地目录中搜索:

#include "myfile.h"

您应该对 EVERY 标头执行的第二件事是:

我的文件名.h:

#ifndef MYFILENAME_H
#define MYFILENAME_H
//put code here
#endif

这种模式意味着你不能在重新定义编译中的标头时摔倒(为orsogufo向我指出这被称为“包含保护”而欢呼)。阅读一下 C 编译器如何实际编译文件(在链接之前),因为这将使 #define 的世界变得很有意义,#include 对你来说很有意义,C 编译器在解析文本时不是很聪明。(然而,C 编译器本身是另一回事)

评论

2赞 Paolo Tedesco 1/21/2009
该模式称为“include guard”。
5赞 xtofl 1/21/2009
大多数编译器都允许等效的#pragma once
3赞 Martin York 1/21/2009
@xtofi:不,他们没有。这是一个 MS 扩展。很少有其他编译器支持它。
5赞 Martin York 1/21/2009
@Spence:不,这是不正确的。第二个 include 还搜索一组目录(只是一组不同的目录)
3赞 Johannes Schaub - litb 1/22/2009
马丁是对的。“”和<>之间的区别是不确定的。它的实现定义了这种差异是什么。但是,大多数编译器在使用 “” 时会首先查看文件相对路径。
16赞 null 1/21/2009 #2
  • 如果你有钱,请查看John Lakos的Large-Scale C++ Software Design。
  • Google C++ 编码指南也有一些 OK 的东西。
  • 在线查看 Sutter Herb 材料(博客)。

基本上,您需要了解哪些地方不需要包含标头,例如。转发声明。还要尝试确保包含文件一个接一个地编译,并且只在必须时才将 #includes 放入 h 文件中(例如模板)。

评论

0赞 MainID 1/21/2009
我认为你所说的就是我需要的。上面的东西很简单,所以每个人都知道。
2赞 Yuval F 1/21/2009 #3

头文件是 C 语言分离接口和实现的方式。它们分为两种类型:标准头文件和用户定义的头文件。 标准头文件(如 string.h)允许我们访问底层 C 库的功能。您应该在每个使用相关功能的 .c 文件中 #include 它。通常,这使用括号,如 #include 用户定义的头文件将函数的实现公开给其他程序员或 C 代码的其他部分。如果您已经实现了一个名为 rational.c 的模块来计算有理数,那么它应该有一个相应的 rational.h 文件用于其公共接口。每个使用该功能的文件都应该 #include rational.h,rational.c 也应该 #include 它。通常这是使用 #include“rational.h”完成的 执行 #includes 的编译部分称为 C 预处理器。它主要进行文本替换和粘贴文本。 Spence 在防止重复 #includes 的模式中是正确的,因为重复会弄乱命名空间。这是包容的基础,GNU Make给了你更多的力量,也给了你更多的麻烦。

-3赞 Raxvan 1/21/2009 #4

使用 if is 在当前工作目录中 如果文件的路径包含在 C++ 包含目录中(在配置中的某个位置,例如:,则必须将路径指定为包含目录) 此外,您还可以包括 .cpp 和 .hpp(h 加号)。 有一组特定的文件可以写成这样:.对于这个特定的示例工作,您需要指定#include "yourfile.h"yourfile.h#include <yourfile.h>yourfile.hc:\mylib\yourfile.hc:\mylib\#include <iostream>using namespace std;

有一个非常好的软件,它与 微软的 Visual C++ 集成 ,并显示包含路径。http://www.profactor.co.uk/includemanager.php

评论

1赞 Wolf 12/13/2017
对于这个特定的示例工作,您需要使用 namespace std 指定;<--这在这里没有多大意义,最好删除这个建议。
3赞 Andy Brice 1/21/2009 #5

除了其他注释之外,请记住,如果只有指针或引用,则无需在另一个标题中 #include 标题。例如:

需要标头:

#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

第二个示例编译速度更快,依赖项更少。这在大型代码库中可能很重要。

评论

0赞 MainID 1/22/2009
谢谢,是所谓的前瞻宣言吗?
0赞 David Thornley 1/22/2009
是的,这是一个前瞻性的声明。对于加快编译速度非常有用。它仅在编译器不需要细节时才起作用;你不能将它用于基类、实际成员之类的东西。
1赞 yesraaj 1/21/2009 #6

查看有关使用 和 C++ 包含 C 库的讨论#include<filename.h>#include<filename>

0赞 xan 1/21/2009 #7

编辑:安迪·布莱斯(Andy Brice)也以更简短的方式提出了这一点。

在 null 的回答中,最重要的事情是你把 #include 放在哪里。

当您编写 #include 预处理器实际上包括您在当前文件中列出的文件的内容,包括这些文件中的任何 #includes。这显然会导致在编译时产生非常大的文件(coad 膨胀),因此您需要仔细考虑是否需要 #include。

在标准代码文件布局中,有一个包含类和函数声明的类的 .h 文件,然后是一个.cpp实现文件,您应该注意头文件中的 #includes 数。这是因为,每次对头文件进行更改时,还必须重新编译包含头文件的任何文件(即使用您的类的文件);如果标头本身有很多包含,那么使用该类的每个文件在编译时都会变得非常膨胀。

最好尽可能使用正向声明,这样可以编写方法签名等,然后在.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.
}

评论

0赞 Wolf 12/13/2017
这将编译,因为 [...]<-- 我不认为这是一个非常有用的评论:要说为什么代码会编译,需要做很多工作。在您的特定情况下,这也是错误的,因为编译器可能已经停止在;)所以你最好之前尝试过真正编译这个......#include utilclass.h
14赞 Martin York 1/21/2009 #8

因此,您的编译器可能支持 2 个唯一的包含文件搜索路径:
非正式地,我们可以将系统称为 include 路径,将用户称为 include 路径。
#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>放在顶部,则编译器将隐藏此错误。

评论

0赞 Wolf 12/12/2017
...哪些编译器支持系统路径与用户路径的区分?看来你在这里只指的是 GCC ......
1赞 Martin York 12/13/2017
@Wolf我引用了该标准(就像 09 年一样)。它表示文件是如何找到的。然后,我将 gcc 实现作为“示例”展示(gcc 是当时占主导地位的 c++ 编译器,clang 存在,但在 09 年几乎没有使用,直到 2011 年才相关,MS cl 编译器似乎只有一个搜索路径)。implementation-defined
0赞 Wolf 12/13/2017
我明白了,谢谢。因此,您将实现定义的位置转换为系统包含路径
0赞 Martin York 12/13/2017
@Wolf 除了用标准中的报价准确回答问题。然后,我举例说明了如何解释实现以解决实际情况。implementation-defined
3赞 Steve Folly 1/22/2009 #9

只是 Andy Brice 回答的附录,您还可以使用函数返回值的正向声明:

class Question;
class Answer;

class UniversityChallenge
{
...
    Answer AskQuestion( Question* );
...
};

这是我不久前问过的一个问题的链接,http://bytes.com/groups/c/606466-forward-declaration-allowed 有一些很好的答案。

0赞 John McFarlane 3/23/2021 #10

有没有关于如何正确使用 #include 的材料?

我强烈推荐 C++ 核心指南SF:源文件部分作为一个很好的起点。

我没有找到任何详细解释这种用法的 C/C++ 教科书。

关于 C++ 项目的物理组合主题的许多传统智慧可以在 John Lakos 的“大规模 C++ 软件设计”中找到。

在正式的项目中,我总是在处理它时感到困惑。

你有很好的同伴。在 C++20 模块之前,是从多个文件组成 C++ 翻译单元的唯一实用方法。它是一种简单、有限的工具,通过它,预处理器基本上将整个文件复制/粘贴到其他文件中。由此产生的编译器输入通常很大,并且工作通常从一个翻译单元重复到另一个翻译单元。#include

0赞 ManuelSchneid3r 7/10/2023 #11

首先决定你是否需要它。从 https://cplusplus.com/articles/Gw6AC542/

  • 如果出现以下情况,则不执行任何操作: A 根本没有引用 B
  • 如果出现以下情况,则不执行任何操作: 对 B 的唯一引用是在好友声明中
  • 如果出现以下情况,则向前声明 B:A 包含 B 指针或引用:B* myb;
  • 如果出现以下情况,则向前声明 B:一个或多个函数具有 B 对象/指针/引用 作为 parementer,或作为返回类型:B MyFunction(B myb);
  • 如果出现以下情况,则 #include“b.h”: B 是 A 的父类
  • 如果出现以下情况,则 #include“b.h”:A 包含 B 对象:B myb;