什么时候应该使用模板而不是继承,反之亦然

When should I use templates instead of inheritance, and vice versa?

本文关键字:继承 反之亦然 什么时候      更新时间:2023-10-16

在许多情况下,这个问题甚至不会问自己,因为有时继承提供了模板无法提供的必要特性。例如,当我需要通过一个基类型(多态性)来处理不同的类型时,我需要使用继承。

然而,在某些情况下,这个问题既可以通过继承解决,也可以通过模板解决。

以代码某些部分的策略模式参数化为例:

文件解析器的一个解决方案可能是这样的:

class FileParser
{
   //...
   public:
      void Parse(ParsingAlgorithm* p);
   //...
}
void FileParser::Parse(ParsingAlgorithm* p)
{
   m_whatevertypeofvaluesineed = p->Parse(whateverparametersyouneed);
}

其中ParsingAlgorithm是一个抽象基类,它提供了一些基本方法,需要被任何想要为FileParser类实现特定解析器的人继承。

然而,同样可以很容易地实现使用模板:

template <class Parser>
class FileParser
{
   //...
   public:
      void Parse()
      {
           m_whatevertypeofvaluesineed = m_parser.Parse(whateverparametersyouneed);
      }
   private:
      Parser m_parser;
   //...
}

是否有一些通用规则可以用来决定是使用模板还是继承?还是应该尽可能地使用模板,以避免虚拟函数之类的运行时开销?

如果您在编译时知道要操作的对象,那么使用模板的静态多态性通常是最快的方法,并且它生成的代码更简洁(不需要显式继承)。它还可以更通用,因为您不受限于强类层次结构。

如果你想要运行时多态性,那么你别无选择,只能使用指针、继承和虚函数的轻微开销。

我个人的看法:

  • 尽可能使用模板,这很舒服
  • 使用继承来分解代码(但不使用异构集合),但要小心切片。
  • 不用担心虚拟调用的性能问题
  • 有时候你没有选择,你想要异构集合拖着使用指针的痛苦

模板提供编译时多态性,而不是继承提供的运行时多态性。如果可以的话,我更喜欢使用模板。

模板通常在运行时性能更好,因为更多的工作是在编译时完成的。另一方面,它们可能更复杂,因此难以书写和理解。因此,当不会使整个解决方案过于复杂时,最好尽可能使用它们。

对于您的示例,它们在功能上并不完全等同:第一个变体允许要求您在运行时创建解析器的实例,而第二个变体要求解析器由程序员在编写代码时选择。

我的建议是在这种情况下都不使用,并为每种文件类型使用单独的函数;

    Stream stream = open(filepath);
    Image image = ParseBMP(stream);
    // ...or
    Image image = ParseJPG(stream);

没必要把事情弄得更复杂