5

背景

我有一个抽象类,比如

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.hTextConverter.h等?所有这些头文件都将包含与IConverter抽象类相同的代码。

有什么想法吗?

4

7 回答 7

8

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

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

如果实现有点长,那么为每个使用单独的 .h 和 .cpp 文件可能更干净。

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

  • 它将保持代码的组织和清洁
  • 减少编译工作:其中一个实现的更改不需要重新编译所有其他实现
  • 更快的编译时间:多个编译器可以一次编译多个文件,例如 Visual Studio 的 /MP 开关。使用多个文件,您将拥有更快的编译时间。
  • 其他文件只能包含他们需要的内容,而不是所有内容
  • 更快的链接时间:由于增量链接,链接时间将减少
  • 使用版本控制,您可以只回顾对特定派生类的更改,而不必通过对庞大的 1 .h/.cpp 文件所做的所有更改来查找特定派生类中的一个更改。
于 2009-02-18T01:44:24.203 回答
2

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

将具体声明与接口声明放在相同的头文件中会破坏这一点,因此现在如果您更改具体类的实现细节,您的客户端将需要重新编译。

于 2009-02-18T04:04:09.437 回答
1

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

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

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

于 2009-02-18T01:42:26.610 回答
1

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

于 2009-02-18T02:04:58.023 回答
1

最好的答案是更容易阅读。对于您和其他程序员来说,一个长的源文件将难以理解。另一方面,许多微小的(半屏全屏)源文件同样糟糕。

于 2009-02-18T02:13:29.297 回答
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 提到的编译器优势。如前所述,您可以在头文件中内联子类定义,但这并不能真正为您带来任何好处。在进行内联等优化时,您的编译器通常比您更聪明。

于 2009-02-18T02:16:00.570 回答
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()
{
    ...
}

呸:-)

于 2009-02-18T10:46:38.283 回答