每个C或C ++文件都应该有一个关联的头文件吗?

时间:2009-03-03 23:06:35

标签: c++ c header-files

每个.C或.cpp文件都应该有一个标题(.h)文件吗?

假设有以下C文件:

  1. MAIN.C

  2. Func1.C

  3. Func2.C

  4. Func3.C

  5. 其中main()位于Main.C文件中。应该有四个头文件

    1. Main.h

    2. Func1.h

    3. Func2.h

    4. Func3.h

    5. 或者所有.C文件应该只有一个头文件?

      什么是更好的方法?

15 个答案:

答案 0 :(得分:30)

拥有main.h是不寻常的,因为在编译时通常不需要向其他编译单元公开。 {/ 1}}本身需要为链接器/启动代码公开,但它们不使用头文件。

每个C文件可以有一个头文件,或者很可能是相关C文件组的头文件。

其中一个例子就是你有一个BTree实现,并且你已经在他们自己的C文件中添加,删除,搜索等,以便在代码更改时最小化重新编译。

在这种情况下,每个C文件都有单独的头文件是没有意义的,因为头是API,即用户库的视图。我怀疑用户是否希望包含6个头文件才能使用这些功能。将有一个main()文件和一个btree.h文件,其中包含从各个C文件构建的所有BTree对象。

另一个例子可以在标准C头中找到。我们不确定btree.lib函数是否有多个C文件(我就是这样做的,但这不是唯一的方法)但是,即使有,也将它们视为一个单元就API而言。您不必包含stdio.hstdio_printf.h等等 - 对于C运行时库的标准I / O部分,只有一个stdio_fgets.h

答案 1 :(得分:10)

  1. 标题文件不是必需的。

  2. #include只需复制/粘贴包含的任何文件(包括.c源文件)

  3. 在现实生活项目中常用的是全局头文件,如config.hconstants.h,其中包含常用信息,如编译时标志和项目范围常量。

  4. 库API的一个好设计是公开一个带有一组头文件的官方接口,并使用一组内部头文件来实现所有细节。这为C库增加了一个很好的额外抽象层,而不会增加不必要的膨胀。

  5. 使用常识。 C / C ++并不适合没有它的人。

答案 2 :(得分:6)

我曾经遵循“依赖”趋势,直到我意识到一致性,一致性和简单性比节省创建文件的努力更重要,并且“即使标准很差,标准也很好”。

我的意思如下:.cpp / .h对文件几乎就是所有“模块”最终的结果。使两者都成为一项要求可以节省大量的混乱和糟糕的工程。

例如,当我在头文件中看到某些东西的界面时,我确切地知道在哪里搜索/放置它的实现。相反,如果我需要公开之前隐藏在.cpp文件中的东西的界面(例如静态函数变为全局),我就知道确切地放置它的位置。

我看到遵循这个简单规则的太多不良后果。不必要的内联函数,打破任何关于封装的规则,(非)分离接口和实现,错位的代码,仅举几例 - 所有这些都是由于从未添加适当的兄弟头或cpp文件这一事实。

所以:总是定义.h和.c文件。使其成为标准,遵循它,并安全依赖。生活这种方式简单得多,简单 是软件中最重要的东西。

答案 3 :(得分:4)

通常最好为每个.c文件提供一个头文件,其中包含要公开的.c文件中的函数声明等。这样,另一个.c文件可以包含它所需函数的.h文件,如果它没有包含的头文件被更改,则不需要重新编译。

答案 4 :(得分:3)

通常每个.c / .cpp文件都会有一个.h文件。

答案 5 :(得分:2)

这取决于。通常你有单独的.c文件的原因将决定你是否需要单独的.h文件。

答案 6 :(得分:2)

我喜欢将接口放入头文件和cpp文件中的实现。我不喜欢编写C ++,我需要在头文件中添加成员变量和原型,然后在C ++中再次添加方法。我更喜欢这样的东西:

module.h中

struct IModuleInterface : public IUnknown
{
    virtual void SomeMethod () = 0;
}

module.cpp

class ModuleImpl : public IModuleInterface,
                   public CObject // a common object to do the reference
                                              // counting stuff for IUnknown (so we
                                              // can stick this object in a smart 
                                              // pointer).
{
    ModuleImpl () : m_MemberVariable (0)
    {
    }

    int m_MemberVariable;

    void SomeInternalMethod ()
    {
        // some internal code that doesn't need to be in the interface
    }

    void SometMethod ()
    {
        // implementation for the method in the interface
    }

    // whatever else we need
};

我发现这是一种非常简洁的分离实现和界面的方法。

答案 7 :(得分:2)

Bjarne Stroustrup在他的“C ++编程语言”一书中详细解释了它....

当程序很小且其部分不打算单独使用时,物理分区的单标题样式最有用。使用命名空间时,程序的逻辑结构仍然可以在单个头文件中解释。

对于较大的程序,单头文件方法在传统的基于文件的开发环境中是行不通的。对公共头的更改会强制重新编译整个程序,并且几个程序员对该单个头的更新很容易出错。除非强调严重依赖命名空间和类的编程风格,否则逻辑结构会随着程序的增长而恶化。

另一种物理组织允许每个逻辑模块都有自己的标头来定义它提供的设施。然后每个.c文件都有一个相应的h。文件指定它提供的内容(其接口)。每个.c模块都包含自己的.h文件,通常还有其他.h文件,用于指定其他模块需要的内容,以实现其界面中公布的服务。该物理组织对应于模块的逻辑组织。多头方法可以轻松确定依赖关系。单头方法迫使我们查看任何模块使用的每个声明,并确定它是否相关。一个简单的事实是,代码的维护总是使用不完整的信息和从本地的角度来完成。 更好的本地化导致编译模块的信息更少,从而加快编译速度。

答案 8 :(得分:1)

没有更好的方法,只有普通和不常见的情况。

更常见的情况是你有一个声明/定义的类/函数接口。最好只有一个带有定义的.cpp / .c和一个用于声明的头文件。 给他们相同的名字很容易理解他们是直接相关的。

但这不是一个“规则”,这是几乎所有情况下的常用方法和最有效方法。

现在在某些情况下(比如模板类或一些微小的结构定义)你不需要任何.c / .cpp文件,只需要标题。我们经常只在头文件中有一些虚拟类接口定义,例如,只有虚拟纯函数或普通函数。

在其他极少数情况下(如假设的main.c / .cpp文件),如果不总是需要允许外部编译单元的代码调用给定编译单元的函数。主要功能是一个示例(不需要标头/声明),但还有其他功能,主要是当它的代码“将所有其他部分连接在一起”并且不被应用程序的其他部分调用时。这是非常罕见的,但在这种情况下,标题毫无意义。

答案 9 :(得分:1)

关键是什么代码需要知道其他代码。您希望将其他文件所知的数量减少到最低限度,以便他们完成工作。

他们需要知道一个函数存在,它们需要传递给它的类型,它将返回什么类型,但不知道它在内部做什么。请注意,从程序员的角度来看,知道这些类型实际意味着什么也很重要。 (例如,哪个int是行,哪个是列)但代码本身并不关心。这就是为什么明智地命名功能和参数是值得的。

正如其他人所说,如果cpp文件中没有任何内容值得暴露给代码的其他部分,就像main.c一样,那么就不需要头文件了。

偶尔值得将所有想要公开的内容放在一个头文件中(例如,Func1and2and3.h),这样任何了解Func1的东西都会知道Func2,但我个人并不热衷于此,因为它意味着你倾向于加载大量垃圾以及你真正想要的东西。

摘要: 想象一下,你相信有人可以编写代码,他们的算法,设计等都很好。你想使用他们编写的代码。所有你需要知道的是让他们得到一些事情,你应该给它什么,以及你会得到什么。这就是头文件中需要的内容。

答案 10 :(得分:1)

如果你的文件暴露了一个接口 - 也就是说,如果它有从其他文件中调用的函数 - 那么它应该有一个头文件。否则,它不应该。

答案 11 :(得分:0)

如前所述,通常每个源(.c或.cpp)文件都有一个标题(.h)文件。

但是,您应该看一下文件的内聚性。如果各种源文件提供单独的,可单独重用的功能集 - 一个理想的组织 - 那么每个文件当然应该有一个头。但是,如果三个源文件提供了一组复合函数(太大而无法放入一个文件中),那么您将使用更复杂的组织。主程序使用的外部服务将有一个标头 - 并且将由需要相同服务的其他程序使用。合作源文件也会使用第二个标题,提供这些文件共享的“内部”定义。

(也由Pax注意到):主程序通常不需要自己的头 - 没有其他源代码应该使用它提供的服务;它使用其他文件提供的服务。

答案 12 :(得分:0)

如果您希望从其他编译单元使用已编译的代码,则需要头文件。在某些情况下,您现在需要/希望拥有标题。

第一个这样的案例是main.c / cpp文件。这个类并不意味着包含在内,因此不需要头文件。

在某些情况下,您可以使用头文件来定义通过在运行时加载的dll加载的一组不同实现的行为。将有不同的.c / .cpp文件集来实现相同标头的变体。这在插件系统中很常见。

答案 13 :(得分:0)

通常cpp / c文件用于实现,而h / hpp(经常不使用hpp)文件用于头文件(仅原型和声明)。 Cpp文件并不总是必须有一个与之关联的头文件,但它通常会起作用,因为头文件就像cpp文件之间的桥梁,所以每个cpp文件都可以使用另一个cpp文件中的代码。

应该强制执行的一件事是在头文件中不使用代码!由于重新定义,头文件在任何大小的项目中都会破坏编译的次数太多了。这就是当您将头文件包含在2个不同的cpp文件中时。头文件应始终设计为包含多次。永远不应该包含Cpp文件。

答案 14 :(得分:0)

一般来说,我认为.h和.c文件之间没有任何明确的关系。在许多情况下(可能是大多数),代码单元是具有公共接口(.h)和不透明实现(.c)的功能库。有时需要一些符号,比如枚举或宏,并且你得到一个没有相应.c的.h,在少数情况下,你会有一堆代码,没有公共接口而没有相应的.h

特别是,为了便于阅读,有很多次,为了程序员的利益,标题或实现(很少两者)都如此庞大和毛茸茸,最终被分成许多较小的文件。理智。