为什么在头文件顶部包含指令

Why are include directives on top of header files?

本文关键字:包含 指令 顶部 文件 为什么      更新时间:2023-10-16

我曾经在一个编程类中被告知,C++通过让程序员在函数块中的任何位置声明其变量,实现了更好的可读性。通过这种方式,变量与处理它的代码部分一起分组

为什么我们不为includes做同样的事情?换句话说,为什么不鼓励将include文件放在实际使用它的定义旁边?

parser::parser()
{
  // some initialization goes there which does not make use of regex
}
#include <boost/regex.hpp>
parser::start()
{
  // here we need to use boost regex to parse the document
}

其中一个原因是#include是无上下文的,它们只是纯文本包含,将其放在代码中间可能会产生一些不必要的影响。例如,考虑您有一个名称空间,并且此文件中的所有代码都属于该名称空间:

// Include on demand:
namespace ns {
   void f() {} // does not need anything
//... lots of other lines of code
#include <vector>
   void g() { std::vector<int> v; }
}

现在,这甚至可能编译得很好。。。而且是错误的。因为包含在名称空间内,所以文件的内容转储在ns内,并且包含的文件声明/定义::ns::std::vector。因为它只是头,它甚至可能编译得很好,但当你试图在具有不同子系统(在不同的命名空间中)的接口中使用它时会失败——这是可以修复的,你只需要关闭所有上下文,添加包含并重新打开相同的上下文。。。

在其他情况下,翻译单元中的代码可能会实际影响include。例如,假设您在翻译单元中添加了using namespace指令。之后包含的任何标头都将有using namespace指令,这也可能产生不必要的效果。

它也可能以不同的方式更容易出错。考虑为intdouble定义不同f过载的两个标头。你可以在文件的开头添加一个(比如int版本)并使用它,然后添加另一个并使用它。现在,如果你在包含第二个标头的行上方调用f(5.0),就会调用int版本——此时编译器只能使用重载——这将是一个很难发现的错误。完全相同的代码行在文件的不同位置会有完全不同的含义(诚然,情况已经如此,但在文件中的声明中,更容易找到哪个是被选中的,以及为什么)

通常,includes声明您将在组件中使用的元素,将它们放在顶部可以快速查看依赖项列表。

想象一下以下文件:

#include <someheader>
namespace myns {
  void foo() {
  }
  void bar() {
    // call something from someheader:
    func();
  }
}

#include <someheader>放在更接近使用点的位置可能很诱人。如果iff你写了以下内容:

namespace myns {
  void foo() {
  }
}
#include <someheader>
namespace myns {      
  void bar() {
    // call something from someheader:
    func();
  }
}

问题是,在中/大型文件中,根据缩进样式,很容易弄不清名称空间(和其他#ifdef)中嵌套的深度。您可能稍后会回来,决定四处移动,或者添加另一个嵌套的命名空间。

所以,如果你总是在顶部写#include,你就永远不会因为意外地写下这样的东西而被咬:

namespace myns {
  void foo() {
  }
// Whoops, this shouldn't be inside myns at all!
#include <someheader>
  void bar() {
    // call something from someheader:
    func();
  }
}

这将介于错误和非常错误之间,这取决于CCD_。(例如,您可能会因违反具有多个定义的ODR而导致UB,尽管这些定义在其他方面合法且相同的令牌序列与不同的功能相匹配,因此违反了§3.2.5)。

这是不鼓励的,因为大多数程序员习惯于在文件顶部包含。我认为习惯的力量,以及与99%现有代码的一致性。

当我个人想看看包括哪些标题时,我只看顶部。在特殊的(谢天谢地是罕见的)情况下,如果有可疑的东西,我可能会在整个文件中查找include。

对编译器来说,这没有什么区别。