清理你的#include语句?

cri*_*ell 28 c++ header file include

如何在C或C++项目中维护#include语句?似乎几乎不可避免的是,最终文件中的include语句集合不足(但由于项目的当前状态而恰好起作用)或包含不再需要的东西.

您是否创建了任何工具来发现或纠正问题?有什么建议?

我一直在考虑编写一些单独编译每个非头文件的东西,每次都删除一个#include语句.继续这样做,直到达到最小的包含集.

为了验证头文件是否包含他们需要的所有内容,我将创建一个源文件,它所做的只是包含头文件并尝试编译它.如果编译失败,那么头文件本身缺少一个include.

在我创作之前,我想我应该问一下.这似乎是一个普遍的问题.

Chr*_*isW 24

为了验证头文件是否包含他们需要的所有内容,我将创建一个源文件,它所做的只是包含头文件并尝试编译它.如果编译失败,那么头文件本身缺少一个include.

通过制定以下规则可以获得相同的效果:foo .c或foo .cpp必须包含的第一个头文件应该是相应命名的foo .h.这样做可以确保foo .h包含编译所需的内容.

此外,Lakos的书" 大规模C++软件设计"(例如)列出了许多用于将实现细节移出标题并进入相应CPP文件的技术.如果你把它发挥到极致,使用Cheshire Cat(隐藏所有实现细节)和Factory(隐藏子类的存在)等技术,那么许多标题将能够独立而不包括其他标题,而​​是使用将声明转发给opaque类型而不是...除了模板类之外.

最后,每个头文件可能需要包括:

  • 没有作为数据成员的类型的头文件(相反,使用"cheshire cat"又名"pimpl"技术在CPP文件中定义/隐藏数据成员)

  • 对于作为参数或从方法返回类型的类型没有头文件(相反,这些类似于预定义类型int;或者,如果它们是用户定义的类型,那么它们是引用,在这种情况下是前向声明的,不透明的类型声明像仅仅class Foo;代替#include "foo.h"在头文件中就足够了).

你需要的是头文件:

  • 超类,如果这是一个子类

  • 可能是用作方法参数和/或返回类型的任何模板化类型:显然你应该能够转发声明模板类,但是某些编译器实现可能有问题(尽管你也可以封装任何模板例如,List<X>作为用户定义类型的实现细节,例如ListX).

在实践中,我可能会创建一个"standard.h",其中包含#define项目中任何/所有头文件使用的所有系统文件(例如STL头,O/S特定类型和/或任何s等),并将其作为每个应用程序头文件中的第一个头文件(并告诉编译器将此"standard.h"视为"预编译头文件").


//contents of foo.h
#ifndef INC_FOO_H //or #pragma once
#define INC_FOO_H

#include "standard.h"
class Foo
{
public: //methods
  ... Foo-specific methods here ...
private: //data
  struct Impl;
  Impl* m_impl;
};
#endif//INC_FOO_H
Run Code Online (Sandbox Code Playgroud)
//contents of foo.cpp
#include "foo.h"
#include "bar.h"
Foo::Foo()
{
  m_impl = new Impl();
}
struct Foo::Impl
{
  Bar m_bar;
  ... etc ...
};
... etc ...
Run Code Online (Sandbox Code Playgroud)

  • 这是对的.但如果您可以使用前向声明,那么只包含头文件是不正确的.因此,对于任何返回值,参数和指针类型,不应包括这些类型的标头,即使编译在没有前向声明类型的情况下也会失败. (2认同)

Sta*_*ked 11

我习惯于将我的包括从高抽象级别排序到低抽象级别.这要求标头必须是自给自足的,并且隐藏的依赖关系会很快显示为编译器错误.

例如,"俄罗斯方块"类有一个Tetris.h和Tetris.cpp文件.Tetris.cpp的包含顺序是

#include "Tetris.h"     // corresponding header first
#include "Block.h"      // ..then application level includes
#include "Utils/Grid.h" // ..then library dependencies
#include <vector>       // ..then stl
#include <windows.h>    // ..then system includes
Run Code Online (Sandbox Code Playgroud)

现在我意识到这并没有真正回答你的问题,因为这个系统并没有真正帮助清理不需要的包含.呃,好吧..


Jos*_*ley 6

这个问题中已经讨论过检测多余的包含.

我不知道有任何工具可以帮助检测不足但发生在工作中的内容,但良好的编码约定可以在这里提供帮助.例如,Google C++样式指南强制要求以下内容,目标是减少隐藏的依赖项:

dir/foo.cc,其主要目的是实现或测试的东西dir2/foo2.h,订购您的包括如下:

  1. dir2/foo2.h (首选地点 - 见下文详情).
  2. C系统文件.
  3. C++系统文件.
  4. 其他库的.h文件.
  5. 你的项目的.h文件.


Set*_*son 6

根据项目的大小,查看doxygen创建的包含图表(INCLUDE_GRAPH选项打开)可能会有所帮助.


Ecl*_*pse 5

删除标头和重新编译技术的一个大问题是它可能导致仍然编译,但错误或低效的代码.

  1. 模板特化:如果您对一个标题中的特定类型具有模板特化,而另一个标题中具有更一般的模板,则删除特化可能会使代码处于可编译状态,但会产生不希望的结果.

  2. 重载分辨率:类似的问题 - 如果你在不同的头文件中有一个函数的两个重载,但是需要稍微兼容的类型,你最终可以删除在一种情况下更适合的版本,但仍然有代码编译.这可能不如模板专业化版本,但它是可能的.