有人熟悉“实现/内部标头”(*.ih)吗?

Jor*_*eit 2 c++ implementation header internal include

在 FB Brokken(The C++ Annotations 的作者)的半年 C++ 课程中,我们学习了所谓的实现头文件的使用。我知道这是弗兰克的约定,但我实际上从未在其他任何地方见过它。因此,我将解释这个概念,我很好奇其他人对此有何看法。

这个想法是,您只需将#include类成员的实现(假设您没有编写类内定义)的实现所需的所有指令放在一个文件中,.ih 实现标头和#include每个源中的此文件文件代替。替代方案是
1)#include类头中的所有内容或
2)#include每个源文件中单独的所有头。

这两种替代方案的缺点是显而易见的:
1a)#include在添加需要一些额外#include' 的任何内容后,您必须重新编译所有包含此标头的源。
1b) 你的头文件,应该是你的类的一个清晰的界面,被一大堆#include指令污染了,用户不知道它们是用来做什么的,他也不关心。2a) 您必须#include在每个源文件中一次又一次地使用相同的标题。
2b) 你的实现被所有这些污染了#include,让它看起来有点乱。

只是要清楚:

/* someclass.h(pp) */

#ifndef SOME_CLASS_H
#define SOME_CLASS_H

class SomeClass
{
    //some private data members
    public:
        SomeClass();
        void sayHi() const;
        // some more member functions
    private:
        // some private member functions
};

#endif

/* someclass.ih */

#include "someclass.h"
#include <iostream>
#include <vector>
using namespace std; 

// namespace is now only used in my implementations, other people
// including my headers won't accidentally import the entire std namespace.

/* sayhi.cc */

#include "someclass.ih"

void SomeClass::sayHi() const
{
     cout << "sayHi() says hi!\n";
}
Run Code Online (Sandbox Code Playgroud)

现在,问题又来了:有没有人听说过这样的约定?我有没有说服任何人开始使用它?我个人认为这是一个非常有用(甚至很明显)的约定,我有点惊讶我没有在其他任何地方看到它。

小智 6

有趣的帖子。让我首先评论 user1428839(以下称为海报)关于 .ih 标头的声明:

首先,海报写道:

...的想法是,您只需将类成员实现的实现所需的所有 #include 指令放在一个文件中,即 .ih 实现头文件中,并将此文件 #include 放在每个源文件中.

按照公式,这不是真的。为避免混淆,句子的最后部分应为: ... in each source file of the class of the .ih file . 在 .ih 方法下,类文件(例如,someclass.h)仅提供接口并且应该只声明可以声明的内容。所以,如果SomeClass有一个类数据成员,std::ofstream *d_out那么就不需要#include <fstream>. 相反,它就足够了#include <iosfwd>。这会产生一个干净的类接口,当被不属于但仅使用SomeClass.

接下来,海报指出:

1a) 在添加需要一些额外#include 的任何内容之后,您必须重新编译所有源#include'ing 这个头文件。

这不是真的。这是如果由附加的头部声明功能在界面中实际使用需要。如果这不是真的,则不需要完全重新编译。如果在类接口中添加了新数据成员或(但这是不好的风格,例如,您使用内联成员)附加标头的元素,需要完全重新编译。

海报提出的下一点:

1b) 你的头文件,应该是你的类的一个清晰的接口,被一大堆#include 指令污染了,用户不知道它们是用来做什么的,他也不关心。

正确,但这里的重点是类头变得太胖了。每次外部源需要包含时someclass.h,编译器还必须读取所有这些额外的头文件,以及那些头文件包含的头文件等,而它只需要知道它们的本质SomeClass是什么。格言是用户不应该为他/她不需要的东西付费,在这种情况下,由于编译器必须读取(通常很多)无用的(在 上下文中SomeClass)头文件而导致额外的编译时间。

使用海报建议的 .ih 约定的替代方法之一是在需要的地方包含您需要的内容。事实上,我认为这是一个很好的选择,但它也有助于大量的簿记和工作。通常类成员实现需要相同的头文件,并将它们放在一个文件中,实现头具有定义一个维护点的额外好处。是的,当编译器偶尔需要读取一个特定源不需要的头文件时,会有一个小的开销,但这只会发生一次:在类编译时,这(希望,大概)只偶尔发生。

恐怕对海报发布的一些反应是对 .ih 方法的核心点略有误解的结果:

  • 类头本身尽可能地保持苗条
  • 使用该类的源不会遭受比严格要求更长的编译时间
  • 类开发人员不必一次又一次地包含编译类成员所需的一堆头文件。相反,只需要包含一个根据类的要求定制的标题。

另一个回复(由 DeadMG)专注于将所有源代码放入一个源文件中。在某种程度上,这是一个风格问题。如果您为一个特定程序开发软件,那么您不妨将所有源代码放在一个文件中,如果这符合您的个人品味。我个人觉得处理这些文件非常烦人和困难,因为我经常喜欢并行查看或处理许多函数的源,但最终这当然是一个品味问题。

但是,如果您开发软件的目的是稍后在其他程序中重用它,例如,您开发一个类并考虑将其添加到库中,那么您绝对应该使用一个功能一个文件的方案。考虑构造函数:类通常提供一系列构造函数,您可以选择适合您上下文的构造函数。如果将类的实现放在一个源文件中,那么您的最终程序将变得过于臃肿,因为链接器必须将实现您的构造函数的目标文件以及该程序所需的目标文件添加到您的程序中,等等,等等。所以你最终得到一个程序,它完全毫无意义地链接到许多附加的目标文件。一个功能一个文件的原则可以防止这种情况发生。