具体类的单独头文件 - C ++

时间:2009-02-18 01:38:37

标签: c++ abstract-class header-files

背景

我有一个抽象类,比如

class IConverter{
    public:
    virtual void DoConvertion() = 0;
};

将会有许多具体的类实现 DoConvertion 方法。

class TextConverter : public IConverter{
    public:
    virtual void DoConvertion(){
         // my code goes here
     }
};

class ImageConverter : public IConverter{
    public:
    virtual void DoConvertion(){
         // my code goes here
     }
};

会有很多这样的具体实现。我创建了一个头文件,例如 CharacterConverter.h ,其中包含抽象类 IConverter

问题

由于我的具体类只是实现 DoConvertion 方法,是否需要为每个具体类创建单独的头文件?我的意思是需要为所有具体类创建 ImageConverter.h TextConverter.h 等等吗?所有这些头文件将包含相同的代码,如 IConverter 抽象类。

有什么想法吗?

7 个答案:

答案 0 :(得分:8)

不是必需的。这基本上是一个判断电话。

如果每个类的实现都很简单,你可以将它们全部放在一个.h和一个.cpp

如果实现时间稍长,那么为每个实现使用单独的.h和.cpp文件可能更简洁。

为每个类使用不同的.h / .cpp的一些优点:

  • 它将保持代码整洁有序
  • 减少编译工作:其中一个实现的更改不需要重新编译所有其他
  • 编译时间更快:多个编译器可以一次编译多个文件,例如Visual Studio的/ MP开关。使用多个文件,您将有更快的编译时间。
  • 其他文件只能包含他们需要的内容而不是一切
  • 更快的链接时间:由于增量链接,链接时间将缩短
  • 使用版本控制,您可以仅回顾对特定派生类的更改,而不必对大量1 .h / .cpp文件进行所有更改,以查找特定派生类中的一个更改。

答案 1 :(得分:1)

你可能会两种方式得到答案。

我会说,对于任何简单的转换器,将它们全部放在单个.h / .cpp对中就足够了,将每一个分成一对就太过分了。我认为在这种情况下,维护大量文件与在一个文件中维护一堆方法的权衡是值得的。

复杂的转换可能值得拥有自己的文件对。

答案 2 :(得分:1)

您需要具体类的定义来创建对象,因此您需要将这些定义放在某个.h文件中。你把它们放在哪个文件取决于你。

答案 3 :(得分:1)

最好的答案是什么更容易阅读。一个很长的源文件对你和其他程序员来说很难跟上。另一方面,许多微小的(半屏幕完整)源文件同样糟糕。

答案 4 :(得分:1)

根据设计的其余部分,你可能会考虑的是一个工厂,你的抽象类有一个静态方法(或多个静态方法,取决于你如何实现它),构造适当的子类并将其作为IConverter *。这样,您可以只在头文件中公开抽象定义,并在单个.cpp文件中包含所有具体的类定义和实现以及超类实现。如果您的子类很大,这会变得有点笨拙,但是如果类较小,则会减少您必须管理的文件数。

但是,正如其他人所指出的那样,这最终是一种判断力。唯一的性能问题与编译有关;更多的cpp文件可能需要(稍微)更长的时间来编译,更多的头文件可能会增加依赖性分析。但是并不要求每个头文件都有匹配的cpp和反之亦然。

根据评论,我建议使用这样的结构:

IConverter.h ==> IConverter的定义
Converters.h ==>所有子类的定义
IConverter.cpp ==>包括IConverter.h和Converters.h,包含IConverter抽象功能的实现(静态工厂方法和任何可继承的功能)
TextConvter.cpp,ImagerConverter.cpp等等==>每个子类的单独cpp文件,每个子文件包含IConverter.h和Converters.h

这使您只能在使用工厂和通用功能的任何客户端中包含IConverter.h。将所有其他定义放在单个标题中,如果它们基本相同,则可以合并。单独的cpp文件允许您利用Brian提到的编译器优势。您可以如上所述内联头文件中的子类定义,但这并不能真正为您带来任何好处。在内联等优化方面,您的编译器通常比您更聪明。

答案 5 :(得分:1)

创建接口类的一个要点是,客户端可以依赖于抽象接口而不是具体实现,然后您可以自由地更改实现而不会影响客户端。

将具体声明放在与接口声明相同的头文件中会使其失败,所以现在如果更改具体类的实现细节,则客户端需要重新编译。

答案 6 :(得分:1)

使用工厂或函数指针可能会更好。

但是,一种特别令人讨厌的方式是使用宏来声明你的具体类。例如:

在IConverter.h的底部包含以下宏

#define DECLARE_CONVERTER_CLASS(CLASS_NAME) \
class CLASS_NAME : public IConverter\
{ \
    public: \
    CLASS_NAME() {} \
    virtual void DoConversion(); \
}; \

然后在MyConverter1.cpp

DECLARE_CONVERTER_CLASS(MyConverter1)

virtual void MyConverter1::DoConversion()
{
    ...
}

Yuck: - )