有人熟悉"implementation/internal header"(*.ih)吗?

Is anyone familiar with the "implementation/internal header" (*.ih)?

本文关键字:ih internal 熟悉 implementation header      更新时间:2023-10-16

在F.B. Brokken(The C++ Annotations的作者)的半年C++课程中,我们被教导使用所谓的实现标头。我知道这是弗兰克的惯例,但我从未在其他任何地方见过它。因此,我将解释这个概念,我很好奇其他人对此有何看法。

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

这两种选择的缺点都很明显:
1a)在添加任何需要额外#include的内容后,您必须重新编译所有源代码#include'ing此标头。
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";
}

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

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

首先,海报写道:

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

按照表述,这不是真的。为避免混淆,句子的最后一部分应为:...在 .ih 文件所属的类的每个源文件中。在.ih方法下,类文件(比如someclass.h)只提供接口,应该只声明可以声明的内容。因此,如果SomeClass具有类数据成员std::ofstream *d_out则无需#include <fstream> .相反,它足以#include <iosfwd>.这会产生一个干净的类接口,当被不属于但仅使用 SomeClass 的源代码包含时,最大限度地减少了其编译时间。

接下来,海报上写着:

1a)添加任何内容后,您必须重新编译此标头 #include 的所有源代码 这需要一些额外的 #include。

那不是真的。仅当接口中实际使用了附加标头声明的功能时,需要执行此操作。如果不是这样,则不需要完全重新编译。如果添加了新的数据成员,或者在类接口中使用了附加标头的元素(但这是不好的样式,例如,您使用的是内联成员),则需要完全重新编译。

海报提出的下一点:

1b)你的头文件,应该是你的类的一个清晰的界面,得到 被一大堆用户不知道的 #include 指令污染 他们被用于,他也不在乎。

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

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

恐怕对海报发布的一些反应是由于稍微误解了.ih方法的核心点:

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

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

但是,如果您开发软件的目的是稍后

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

我从未在任何地方看到过使用它。大多数类将所有实现的函数都放在一个文件中,因此它们在重复指定必要的标头时没有问题。

我不明白。 当您拥有使用来自其他标头的类或支柱或类型定义的类成员或类方法参数时,会发生什么情况? 并且,加上重新编译的问题...这似乎是为了获得更多的工作。 您应该只在标头中包含标头所需的内容,并且只包含实现在 impl 中需要的内容。 就是这样。

相关文章: