在F.B.的半年C ++课程中。 Brokken(The C ++ Annotations的作者),我们已经学习了使用所谓的实现头文件。我知道这是弗兰克的惯例,但我从未在其他任何地方看到它。因此,我将解释这个概念,我对其他人对此的看法非常好奇。
我们的想法是,您只需将类成员实现所需的所有#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";
}
现在,问题是:有没有人听说过这样的惯例?我是否说服任何人开始使用它?我个人认为这是一个非常有用(甚至是显而易见的)惯例,我有点惊讶,我没有在其他任何地方看到它。
答案 0 :(得分:4)
有趣的帖子。让我首先评论一下有关.ih标题的user1428839(以下简称海报)语句:
首先,海报写道:
......我们的想法是你只需要输入所需的所有#include指令 你的类成员实现的实现......在一个文件中,.ih 实现头,并在每个源文件中#include此文件。
根据公式,这不是真的。为避免混淆,句子的最后部分应为:... .ih文件所属类的每个源文件 。在.ih方法下,类 header 文件(比如,someclass.h
)仅提供接口,并且只应声明可以声明的内容。因此,如果SomeClass
有一个班级数据成员std::ofstream *d_out
,那么就不需要#include <fstream>
。相反,它足以#include <iosfwd>
。这样就形成了一个干净的类接口,最大限度地缩短了编译时间,当它包含在不属于的资源时,只使用SomeClass
。
接下来,海报说:
1a)你必须在添加任何内容后重新编译所有来源#include'ing this header 这需要一些额外的#include's。
这不是真的。如果附加标头声明的功能实际在接口中使用,则仅是必需的。如果不是这样,则不需要完全重新编译。如果添加了新的数据成员,则需要完全重新编译 ;或者(但这样的样式,例如,您正在使用内联成员),在类接口中使用附加标头的元素。
海报提出的下一点:
1b)你的头文件应该是你班级的清晰界面 受到大量#include指令的污染,用户根本不知道是什么 他们习惯了,他也不关心。
正确,但重要的是这个类标题变得太胖了。每当外部源需要包含someclass.h
时,编译器还必须读取所有这些额外的头文件以及那些头文件中包含的内容,等等,而只需要知道SomeClass
的基本内容。谚语是用户不必为他/她不需要的东西付费,在这种情况下,编译器必须阅读(通常很多)无用的额外编译时间(在SomeClass
的上下文中)头文件。
使用海报建议的.ih惯例的替代方法之一是在您需要的地方包含您需要的内容。事实上,我认为这是一个很好的选择,但它也会带来大量的簿记和工作。通常类成员实现需要相同的头文件,并将它们放在一个文件中,实现头具有定义一个维护点的额外好处。是的,偶尔编译器必须读取特定源不需要的头时会有一个小的开销,但只发生一次:在类编译时,(希望,可能)偶尔只会发生。
我担心海报发布的一些反应是对.ih方法的核心要点略有误解的结果:
另一个回复(由DeadMG)专注于将所有来源放入一个源文件中。在某种程度上,这是一种风格问题。如果您为某个特定程序开发软件,那么您可以将所有资源放入一个文件中,如果这符合您的个人喜好。我个人发现非常令人烦恼并且很难处理这些文件,因为我经常喜欢并行地看到或使用许多功能的来源,但最后它当然是品味的问题。
但是,如果您开发软件的目的是在以后的其他程序中重用它,例如,您开发了一个类,想到将它添加到库中,那么您肯定应该使用单功能一个 - 文件方案。考虑构造函数:类通常提供一系列构造函数,并选择适合您的上下文的构造函数。如果将类的实现放入一个源文件中,那么最终的程序将会过度繁重,因为链接器必须将实现构造函数的目标文件添加到程序中,以及该程序所需的目标文件,等等。所以你最终得到的程序完全毫无意义地链接到许多额外的目标文件。单功能单文件原则可以防止这种情况发生。
答案 1 :(得分:3)
我从来没有见过这种用过的东西。大多数类都将所有实现的函数都放在一个文件中,因此它们在重复指定必要的标题时没有问题。
答案 2 :(得分:1)
我不明白。当你有类成员或类方法参数使用其他标题中的类或struts或typedef时会发生什么?并且,添加重新编译的问题......这似乎更多的工作,以获得收益。您应该只包含标头中标头所需的内容,并且只包含impl中实现所需的内容。就是这样。