【问题标题】:where should "include" be put in C++“包含”应该放在 C++ 中的什么位置
【发布时间】:2011-01-18 20:20:00
【问题描述】:

我正在阅读一些 c++ 代码,请注意头文件和 .cpp 文件中都有“#include”。我想如果我将文件中的所有“#include”(比如说 foo.cpp)移动到它的头文件 foo.hh 并让 foo.cpp 只包含 foo.hh 代码应该可以正常工作,不考虑诸如此类的问题缺点 , 效率 等等 .

我知道我的“突然”想法在某种程度上肯定是个坏主意,但它的确切缺点是什么?我是 C++ 新手,所以在我自己回答这个问题之前,我不想阅读大量 C++ 书籍。因此,请在此处提出问题以寻求帮助。提前致谢。

【问题讨论】:

    标签: c++ include


    【解决方案1】:

    你可以让包括你的头文件在内的所有其他文件在你的头文件中也包含所有#includes。

    在 C++ 中(如在 C 中)#include 由预处理器处理,只需将所有文本插入 #included 文件中代替 #include 语句即可。因此,使用大量#includes,您可以将可编译文件的大小夸大到数百千字节——编译器需要为每个文件解析所有这些。请注意,包含在不同位置的同一个文件必须在它为#included 的每个位置再次重新解析!这会减慢编译速度。

    如果您需要在标题中声明(但不定义)内容,请使用 forward declaration 而不是 #includes。

    【讨论】:

    • 或者,更准确地说,尽可能在您的 .h 文件中使用前向声明而不是 #include。有时这是不可避免的。例如,如果 A 类具有 B 类的数据成员,则 A.h 必须包含 B.h。
    【解决方案2】:

    作为一项规则,尽可能将您的包含放入 .cpp 文件中,并且仅在不可能的情况下放入 .h 文件中。

    在许多情况下,您可以使用forward declarations 来消除从其他头文件中包含头文件的需要:这有助于减少编译时间,这会随着项目的增长而成为一个大问题。这是一个尽早养成的好习惯,因为试图在以后(当它已经是一个问题)解决它可能是一场彻头彻尾的噩梦。

    这个规则的例外是模板类(或函数):为了使用它们,您需要查看完整的定义,这通常意味着将它们放在头文件中。

    【讨论】:

    • 你对“extern template”的解读是非常一厢情愿的。你会失望的。
    • @nobugz:哦?真的吗?该死的,我希望它不是……模板编译速度是 C++ 最糟糕的事情之一。能够对那些不需要看到它们的人隐藏实现将是一个真正的福音。
    • 听起来您将新的 extern 模板与 export 混淆了(几乎已被放弃)。 cppreference.com/wiki/keywords/export
    【解决方案3】:

    如果您#include .cpp 文件,您最终可能会收到来自链接器的大量“多重定义”错误。理论上,您可以将所有内容#include 到单个翻译单元中,但这也意味着每次更改单个文件时都必须重新构建所有内容。对于现实世界的项目,这是不可接受的,这就是我们有链接器和 make 等工具的原因。

    【讨论】:

    • @jkp .cpp 文件不是标题
    • ?他不是在谈论#include .cpp 文件,而是谈论将所有#include 从.cpp 放入.hpp。也不是一个好主意,但就像#include .cpp 文件一样邪恶。
    【解决方案4】:

    在头文件中包含头文件很好,在 c++ 文件中包含头文件也很好,但是,为了最大限度地减少构建时间,通常最好避免在另一个头文件中包含头文件,除非绝对必要,特别是如果许多 c++ 文件包含相同的标题。

    【讨论】:

      【解决方案5】:

      标头中的包含文件应仅是支持该标头所必需的文件。例如,如果你的头文件声明了一个向量,你应该包含向量,但没有理由包含字符串。您应该能够拥有一个只包含该单个头文件并将编译的空程序。

      当然,在源代码中,您调用的所有内容都需要包含在内。如果您的任何标头都不需要 iostream,但实际源需要它,则应单独包含它。

      在我看来,包含文件污染是最糟糕的代码腐烂形式之一。

      编辑:呵呵。看起来解析器吃掉了 > 和

      【讨论】:

        【解决方案6】:

        .hh(或.h)文件应该用于声明。

        .cpp(或 .cc)文件应该用于定义和实现。

        首先意识到#include 语句是文字#include "foo.h" 从字面上复制 foo.h 的内容并将其粘贴到另一个文件中包含指令的位置。

        这个想法是其他一些文件 bar.cpp 和 baz.cpp 可能想要使用 foo.cc 中存在的一些代码。通常,这样做的方法是将 bar.cpp 和 baz.cpp 发送到 #include "foo.h" 以获取他们想要使用的函数或类的声明,然后在链接时,链接器将连接这些用途从 bar.cpp 和 baz.cpp 到 foo.cpp 中的实现(这就是链接器的重点)。

        如果您将所有内容都放在 foo.h 中并尝试这样做,您就会遇到问题。假设 foo.h 声明了一个名为 doFoo() 的函数。如果这个函数的定义(代码)在 foo.cc 中,那很好。但是如果将doFoo() 的代码移到 foo.h 中,然后将 foo.h 包含在 foo.cpp、bar.cpp 和 baz.cpp 中,则名为 doFoo() 的函数现在有三个定义,而你的链接器会抱怨,因为您不允许在同一范围内拥有多个具有相同名称的事物。

        【讨论】:

          【解决方案7】:

          如果使用“包含保护”,可以避免多个定义错误。

          (begin myheader.h)
          #ifndef _myheader_h_
          #define _myheader_h_
          struct blah {};
          extern int whatsit;
          #endif //_myheader_h_
          

          现在如果你在其他头文件中#include "myheader.h",它只会被包含一次(由于 _myheader_h_ 被定义)。我相信 MSVC 有一个具有等效功能的“#pragma once”。

          【讨论】:

          • #pragma once 现在是事实上的标准 - gcc 也支持它,我相信其他一些人也支持。
          【解决方案8】:

          在头文件中使用#include 没有任何问题。这是一种非常常见的做法,您不想让用户负担一个库,同时记住还需要什么其他晦涩的标题。

          一个标准的例子是#include <vector>。为您提供矢量类。以及大量正确编译矢量类所需的内部 CRT 头文件,这些东西你真的不需要也不想知道。

          【讨论】:

            【解决方案9】:

            虽然头文件应该只包含它需要的东西,但“它需要的东西”比您想象的更流畅,并且取决于您放置标头的目的。我的意思是,某些标头实际上是库或其他代码的接口文档。在这些情况下,标头必须包含(并且可能是#include)其他开发人员需要的所有内容才能正确使用您的库。

            【讨论】:

              猜你喜欢
              • 2011-08-18
              • 1970-01-01
              • 1970-01-01
              • 2012-05-26
              • 1970-01-01
              • 2011-12-26
              • 2018-03-19
              • 1970-01-01
              • 2010-11-05
              相关资源
              最近更新 更多