代码之家  ›  专栏  ›  技术社区  ›  paercebal

C++包含语义

  •  34
  • paercebal  · 技术社区  · 17 年前

    对于同一预处理指令,这是一个多问题。

    1-<&燃气轮机;还是?

    除了MSDN中的信息外:

    #include Directive (C-C++)

    这两种符号有什么区别?
    1.b:所有编译器的实现方式都一样吗?
    1.c:您什么时候使用<>,您何时会使用“”(即,您将使用哪种标准作为标题include的一个或另一个)?

    2-#包括{TheProject/TheHeader.hpp}或{TheHeader.hpp}?

    我见过至少两种写项目标题的方法。 考虑到您至少有4种类型的标头,即:

    • 项目的私有标题?
    • 项目的标题,但正在导出符号(因此为“公共”)
    • 模块链接到的另一个项目的标题
    • 编译器或标准库的标题

    对于每种标题:

    2.a:您会使用<&燃气轮机;还是?
    2.b:您是将其包含在{TheProject/TheHeader.hpp}中,还是仅包含在{TheHeader.hpp}中?

    3-奖金

    3.a:您是否在树状组织(即目录中的目录,而不是“一个目录中的每个文件”)中使用源和/或标题进行项目工作,其优点/缺点是什么?

    10 回复  |  直到 6 年前
        1
  •  35
  •   Community Mohan Dere    6 年前

    在阅读了所有答案以及编译器文档之后,我决定遵循以下标准。

    对于所有文件,无论是项目头还是外部头,始终使用以下模式:

    #include <namespace/header.hpp>
    

    命名空间至少有一个目录深,以避免冲突。

    当然,这意味着项目头所在的项目目录也应该作为“默认包含头”添加到makefile中。

    选择此选项的原因是我发现了以下信息:

    1.include“”模式依赖于编译器

    我将在下面给出答案

    1.a符合标准

    资料来源:

    在第16.2节“源文件包含”中,我们可以看到:

    表单的预处理指令

      #include <h-char-sequence> new-line
    

    在实现定义的位置序列中搜索由<及>分隔符,并导致将该指令替换为标头的全部内容。如何指定位置或标识标头是实现定义的。

    这意味着#包括<&燃气轮机;将以实现定义的方式搜索文件。

    然后,下一段:

    表单的预处理指令

      #include "q-char-sequence" new-line
    

    导致用“分隔符”之间的指定序列标识的源文件的全部内容替换该指令。将以实现定义的方式搜索命名的源文件。如果不支持此搜索,或搜索失败,则将重新处理该指令,就像它读取一样

    #包括<h-字符序列>换行
    

    具有与原始指令相同的包含序列(包括>字符,如果有)。

    这意味着#包括“…”将以实现定义的方式搜索文件,然后,如果找不到该文件,将进行另一次搜索,就像它是一个#include<&燃气轮机;

    结论是我们必须阅读编译器文档。

    请注意,由于某些原因,在标准中,“系统”或“库”标题或其他标题之间没有区别。唯一的区别似乎是#包括<&燃气轮机;似乎以标题为目标,而#包括“…”似乎是针对源代码(至少在英语中是这样)。

    1.b Visual C++:

    资料来源:

    #包括“MyFile.hpp”

    预处理器按以下顺序搜索包含文件:

    1. 与包含#include语句的文件位于同一目录中。
    2. 在以前打开的任何文件的目录中,包括与打开顺序相反的文件。搜索从上次打开的include文件的目录开始,然后继续搜索首先打开的include文件的目录。
    3. 沿着每个/I编译器选项指定的路径。
    4. (*)沿着INCLUDE环境变量或开发环境默认INCLUDE指定的路径。

    #包括<我的文件。水电站>

    预处理器按以下顺序搜索包含文件:

    1. 沿着每个/I编译器选项指定的路径。
    2. (*)沿着INCLUDE环境变量或开发环境默认INCLUDE指定的路径。

    请注意最后一步

    文档不清楚这两个变量的“沿INCLUDE环境变量指定的路径”部分 <...> "..." 包括。以下引用使其符合标准:

    对于指定为#include“path spec”的include文件,目录搜索从父文件的目录开始,然后继续搜索任何祖辈文件的目录。也就是说,搜索相对于包含正在处理的#include指令的源文件的目录开始。如果没有祖父母文件且未找到该文件,则继续搜索,就像文件名被括在尖括号中一样。

    因此,最后一步(用星号标记)是阅读整个文档的解释。

    1.c.g++

    资料来源:

    以下引文总结了该过程:

    GCC[…]将使用#include查找请求的标题 <file> 在[系统目录][…]中在默认目录之前,将按从左到右的顺序搜索以-I命名的所有目录

    GCC首先在包含当前文件的目录中查找用#include“file”请求的头,然后在由-iNote选项指定的目录中查找,然后在相同的位置查找用尖括号请求的头。

    #包括“MyFile.hpp”

    此变体用于您自己程序的头文件。预处理器按以下顺序搜索包含文件:

    1. 与包含#include语句的文件位于同一目录中。
    2. 沿着每个-iNote编译器选项指定的路径。
    3. 至于#包括 <MyFile.hpp>

    #包括<我的文件。水电站>

    此变体用于系统头文件。预处理器按以下顺序搜索包含文件:

    1. 沿着每个-I编译器选项指定的路径。
    2. 在系统目录中。

    1.d Oracle/Sun Studio CC

    资料来源:

    请注意,文本本身有些矛盾(请参见示例以了解)。关键短语是: 不同之处在于,当前目录只搜索名称用引号括起来的头文件。 "

    #包括“MyFile.hpp”

    此变体用于您自己程序的头文件。预处理器按以下顺序搜索包含文件:

    1. 当前目录(即,包含–include–文件的目录)
    2. 使用-I选项命名的目录(如果有)
    3. 系统目录(例如/usr/include目录)

    #包括<我的文件。水电站>

    此变体用于系统头文件。预处理器按以下顺序搜索包含文件:

    1. 使用-I选项命名的目录(如果有)
    2. 系统目录(例如/usr/include目录)

    1.e XL C/C++编译器参考-IBM/AIX

    资料来源:

    这两个文档的标题都是“XL C/C++编译器参考”。第一个文档较旧(8.0),但更容易理解。第二个版本较新(12.1),但解密起来有点困难。

    #包括“MyFile.hpp”

    此变体用于您自己程序的头文件。预处理器按以下顺序搜索包含文件:

    1. 当前目录(即包含包含文件的目录)
    2. 使用-I选项命名的目录(如果有)
    3. 系统目录(例如/usr/vac[cpp]/include或/usr/include目录)

    #包括<我的文件。水电站>

    此变体用于系统头文件。预处理器按以下顺序搜索包含文件:

    1. 使用-I选项命名的目录(如果有)
    2. 系统目录(例如/usr/vac[cpp]/include或/usr/include目录)

    1.e结论

    模式“可能导致编译器间的编译错误,并且我目前在Windows Visual C++、Linux G+、Oracle/Solaris CC和AIXXL上都工作,这是不可接受的。

    无论如何,“”所描述的功能的优点一点也不有趣,所以。。。

    2.使用{namespace}/头。水电站模式

    我在工作中看到( i、 这不是理论,这是现实生活中痛苦的职业经历 )两个名称相同的头,一个在本地项目目录中,另一个在全局include中。

    由于我们使用的是“”模式,并且该文件同时包含在本地头和全局头中,所以当出现奇怪的错误时,无法理解到底发生了什么。

    使用include中的目录可以节省我们的时间,因为用户必须写入:

    #include <MyLocalProject/Header.hpp>
    

    #include <GlobalInclude/Header.hpp>
    

    你会注意到

    #include "Header.hpp"
    

    因此,本可以成功编译,但仍然隐藏问题

    #include <Header.hpp>
    

    在正常情况下不会编译。

    因此,坚持<&燃气轮机;注释将强制开发人员在include前面加上正确的目录,这是另一个选择<&燃气轮机;至“。

    3.结论

    同时使用<&燃气轮机;表示法和名称空间表示法一起从预编译器中消除了猜测文件的可能性,而不是只搜索默认的include目录。

    当然,标准库仍然像往常一样包括在内,即:

    #include <cstdlib>
    #include <vector>
    
        2
  •  7
  •   Evan Teran    17 年前

    我通常使用<&燃气轮机;用于系统标题,而“”用于项目标题。至于路径,只有当您想要的文件位于包含路径的子目录中时,才需要这样做。

    例如,如果您需要/usr/include/SDL/中的一个文件,但include路径中只有/usr/include/,那么您可以使用:

    #include <SDL/whatever.h>
    

    另外,请记住,除非放置的路径以/开头,否则它是相对于当前工作目录的。

    编辑以回答注释:这取决于,如果一个库只有几个包含,我只会在包含路径中包含它的子目录,但是如果库有许多头(比如几十个),那么我更喜欢将它放在我指定的子目录中。Linux的系统头就是一个很好的例子。您使用它们的方式如下:

    #include <sys/io.h>
    #include <linux/limits.h>
    

    编辑以包含另一个好的答案:另外,如果可以想象两个或多个库以相同的名称提供头,那么子目录解决方案基本上为每个头提供一个名称空间。

        3
  •  5
  •   Michael Burr    17 年前

    引用C99标准(乍一看,C90标准中的措辞似乎完全相同,但我无法从中剪切n-paste):

    表单的预处理指令

    # include "q-char-sequence" new-line

    导致更换该设备 指令的全部内容 由 在“”之间指定的顺序 分隔符。命名的源文件为 寻找 实现定义方式。如果这 不支持搜索,或者如果 搜索失败,指令无效 重新处理,就像它读取

    # include <h-char-sequence> new-line

    具有相同的包含序列 (包括>个字符,如果有)来自 原始指令。

    所以搜索的地点 #include "whatever" 是由搜索的位置的超集合 #include <whatever> 。目的是第一种样式将用于通常“属于”您的头,第二种方法将用于“属于”编译器/环境的头。当然,通常会有一些灰色区域——例如,您应该使用哪个区域作为Boost头?我会用 #include <> ,但如果我的团队中有人想要,我不会争论太多 #include "" .

    在实践中,我认为只要构建没有中断,就不会有人关注使用哪种形式。我当然不记得有人在代码评审中提到过它(甚至不记得)。

        4
  •  3
  •   Trent    17 年前

    我将回答你问题的第二部分:

    我通常使用 <project/libHeader.h> 当我包含来自第三方的标题时。和 "myHeader.h" 在项目中包含标题时。

    我使用 <项目/libHeader。h> 而不是 <libHeader.h> 是因为可能有多个库具有“libHeader.h”文件。为了包含它们,您需要库名称作为包含的文件名的一部分。

        5
  •  2
  •   user3458 user3458    17 年前

    这两种符号有什么区别?

    “”在C/C++文件所在的目录中开始搜索<&燃气轮机;在-I目录和默认位置(例如/usr/include)中启动搜索。它们最终都搜索同一组位置,只是顺序不同。

    1.b:所有编译器的实现方式都一样吗?

    我希望如此,但我不确定。

    1.c:您什么时候使用<>,您何时会使用“”(即,您将使用哪种标准作为标题include的一个或另一个)?

    当include文件应该位于C文件的旁边时,我使用“”,<&燃气轮机;在所有其他情况下。特别是,在我们的项目中,所有“public”include文件都位于project/include目录中,因此我使用<&燃气轮机;为了他们。

    2-#包括{TheProject/TheHeader.hpp}或{TheHeader.hpp}?

    如前所述,xxx/filename。h允许您执行诸如diskio/ErrorCodes之类的操作。h和网络/错误代码。H

    *项目的私有标题?

    项目中我的子系统的私有标头。使用“filename.h” 项目中我的子系统的公共头(在项目外部不可见,但其他子系统可以访问)。根据适用于项目的惯例,使用或。我宁愿使用

    *项目的标题,但正在导出符号(因此为“公共”)

    包含的内容与库的用户包含的内容完全相同。可能

    *模块链接到的另一个项目的标题

    由项目决定,但一定要使用<&燃气轮机; *编译器或标准库的标题 肯定<>,根据标准。

    3.a:您是否在树状组织(即目录中的目录,而不是“一个目录中的每个文件”)中使用源和/或标题进行项目工作,其优点/缺点是什么?

    我做一个结构化的项目。一旦你有超过20个文件,一些分歧就会变得明显。你应该按照代码引导你的方式去做。

        6
  •  1
  •   J.J.    17 年前

    如果我没记错的话。

    在“路径”中可以找到的所有库都使用菱形。因此,STL中的任何库,或您已安装的库。在Linux中,您的路径通常是“/usr/include”,在windows中,我不确定,但我猜它在“C:\windows”下。

    然后使用“”指定其他所有内容。没有起始目录信息的“my_bla.cpp”将解析为代码所在/编译的目录。或者,您也可以指定包含的确切位置。像这样的“c:\myproj\some\u code.cpp”

    标题的类型并不重要,只是位置。

        7
  •  1
  •   John Dibling    17 年前

    Re<&燃气轮机;vs”。在我的店里,就“风格”而言,我是非常随便的。我有一个要求的为数不多的领域之一是在#include语句中使用尖括号——规则是:如果包含操作系统或编译器文件,可以在适当的情况下使用尖括号。在所有其他情况下,它们都是被禁止的。如果您包含由此处某人或第三方库编写的文件,<&燃气轮机;这是禁止的。

    原因是:#include“x.h”和#include不搜索相同的路径#include将只搜索系统路径和您输入的任何内容。重要的是,如果该目录没有以其他方式包含在搜索路径中,它将不会搜索文件x.h所在的路径。

    例如,假设您有以下文件:

    c:\dev\angles\main。cpp

    #include "c:\utils\mylib\mylibrary.h"
    
    int main()
    {
        return 0;
    }
    

    c:\utils\mylib\mylibrary。H

    #ifndef MYLIB_H
    #define MYLIB_H
    
    #include <speech.h>
    
    namespace mylib
    {
        void Speak(SpeechType speechType);  
    };
    
    #endif
    

    c:\utils\mhlib\speech。H

    #ifndef SPEECH_H
    #define SPEECH_H
    
    namespace mylib
    {
        enum SpeechType {Bark, Growl};
    };
    
    #endif
    

    如果不通过设置path环境变量或c:\utils\mhlib\目录中的-i'ing来更改路径,则无法编译此文件。编译器将无法解析 #include <speech.h> 即使该文件与 mylibrary.h !

    我们在代码中的#include语句中大量使用相对和绝对路径名,原因有二。

    1) 通过使库和组件远离主源代码树(即,将实用程序库放在特殊目录中),我们不需要;t将库的生命周期与应用程序的生命周期耦合。当您有几个使用公共库的不同产品时,这一点尤为重要。

    2) 我们使用 Junctions 要将硬盘驱动器上的物理位置映射到逻辑驱动器上的目录,然后在all#includes中使用逻辑驱动器上的完全限定路径,请执行以下操作。例如:

    #include "x:\utils\mylib.h" --很好,x:是一个替代驱动器,x:\utils指向硬盘上的c:\code\utils\u 1.0

    #include "c:\utils_1.0\mylib.h" --糟糕!该应用程序包括mylib。h现在与MYLIB库的特定版本相耦合,所有开发人员都必须将它放在硬盘驱动器c:\utils\u 1.0的同一目录中

    最后,我的团队的一个广泛但难以实现的目标是能够支持一键编译。这包括只需从源代码管理中获取代码,然后点击“compile”即可编译主源代码树。特别是,我讨厌必须设置路径&机器范围内#包括目录以便能够编译,因为在buildign开发机器的设置阶段添加的每一个额外步骤都会使它更难、更容易搞乱,并且需要更长的时间才能使新机器加速&生成代码。

        8
  •  1
  •   Kim Gräsman    10 年前

    两者之间有两个主要区别 <> "" 。第一个是名称的结尾字符-标题名称中没有转义序列,因此您可能会被迫这样做 #include <bla"file.cpp> "bla>file.cpp" .不过,这可能不会经常出现。另一个区别是系统包含不应该发生在 "" 只是 <> 所以 #include "iostream" 不保证工作; #include <iostream> 是我个人的偏好是使用 "" 对于属于项目一部分的文件,以及 <> 对于不是的文件。有些人只使用 <> 对于标准库标题和 "" 为了其他一切。有些人甚至使用 <> 仅适用于Boost和std;这取决于项目。像所有风格方面一样,最重要的是保持一致。

    对于路径,外部库将指定头的约定;例如 <boost/preprocessor.hpp> <wx/window.h> <Magic++.h> 。在本地项目中,我会编写与顶级srcdir相关的所有路径(或者在不同的库项目中,写入include目录)。

    编写库时,您可能会发现使用<&燃气轮机;区分私有标头和公共标头,或不区分 -I 源目录,但上面的目录,所以 #include "public_header.hpp" "src/private_header.hpp" .这真的取决于你。

    编辑:对于具有目录结构的项目,我强烈推荐它们。想象一下,如果所有boost都在一个目录中(并且没有子名称空间)!目录结构很好,因为它让您更容易找到文件,并允许您在命名方面更灵活( "module\_text\_processor.hpp" 相对于 "module/text\_processor.hpp" )。后者更自然,更容易使用。

        9
  •  0
  •   James Curran    17 年前

    我使用<&燃气轮机;从系统头文件(stdio、iostreams、字符串等)和“…”用于特定于该项目的标题。

        10
  •  0
  •   Brian Stewart    17 年前

    我们在解决方案中使用#include“header.h”作为本地项目的标题,使用#include作为系统包含、第三方包含和其他项目的标题。我们使用visualstudio,在头include中使用项目目录要容易得多,这样每当我们创建新项目时,我们只需为包含所有项目目录的目录指定include路径,而不是为每个项目指定单独的路径。