【问题标题】:When to use template vs inheritance何时使用模板与继承
【发布时间】:2011-11-08 00:40:58
【问题描述】:

我一直在寻找这个,对此的普遍反应似乎是“它们不相关,一个不能替代另一个”。但是假设你在面试中被问到“你什么时候会使用模板而不是继承,反之亦然?”

【问题讨论】:

  • 这怎么会“不是一个真正的问题”?快乐的扳机手指让我很恼火。
  • 如果你在采访中听到这个问题,是时候说再见了,因为他们只是在浪费你的时间;你现在对我们的也一样。
  • 这是一个愚蠢的面试问题。充其量,这是错误的方法。你不应该问“你什么时候使用工具 X”。你应该问,“这是问题 J。你如何解决它?”答案可能是“工具 X”。
  • 这实际上是一个很好的问题。它测试程序员抽象思考问题类别而不是“给定”问题的能力。找到这个问题后,问的内容就很清楚了,一点也不含糊。轨道声明中的第二次轻量级竞赛。触发愉快的关闭是边界拖钓。

标签: c++ templates inheritance


【解决方案1】:

我认为模板和继承实际上是正交的概念:继承是“垂直的”,并且从抽象到越来越具体。一个形状,一个三角形,一个等边三角形。

另一方面,模板是“水平的”,定义了并行的代码实例,彼此之间一无所知。整数排序在形式上与双精度数排序和字符串排序相同,但它们是三个完全不同的函数。从远处看,它们都“看起来”一样,但它们彼此无关。

继承提供运行时抽象。模板是代码生成工具。

由于这些概念是正交的,因此可以很高兴地将它们一起使用以实现共同目标。我最喜欢的例子是 type erasure,其中类型擦除容器包含一个指向实现类的虚拟基指针,但有任意多个由模板派生类生成的具体实现。模板代码生成用于填充继承层次结构。魔法。

【讨论】:

  • 我喜欢这个解释。比其他的更通俗一点。
  • 嗯,我不是这方面的专家,这就是我找到这个答案的原因,但正如你所描述的那样,在我看来,模板解决了复杂的继承系统缺乏 C++ 的问题,例如mixins(例如,Go 优雅地解决了一个问题)。如果整数、双精度和字符串没有共同点但需要排序,那么这三个都应该以某种方式扩展为“可排序”特征。
  • @pistacchio:我不会将复杂继承称为“解决方案”。模板以更好的方式提供可排序性,因为排序者不需要知道它正在被排序。当您以一种强迫思维方式工作时,通常会导致复杂的继承,即一切都必须是继承层次结构,这实际上很少合适。 Go 的方法很简单,但限制更多,效率更低。它适合 Go 想要成为的小语言。
  • 我想挑战这个。您正在描述抽象,它只是将不同的行为参数化。对于模板,参数是静态评估的,对于继承,它是动态评估的(你可以设想一个纯虚拟接口'Sortable'——当然,在C++中,原语不能满足接口,所以你需要做一些装箱来排序ints 之类的,但这与继承无关(相反,限制是由 C++ 的继承实现引入的)。另外值得注意的是:“继承”可能是一个不好的术语;我们应该说“多态性”。跨度>
  • @KerrekSB Go 的方法也不需要排序者知道正在排序的内容;例如,time.Time 不知道它是可排序的,但它提供了一个 'Before' 方法(类似于 'less()'),因此它是可排序的。模板通常更适合排序之类的事情,因为运行时多态性通常意味着每个元素的运行时成本很小,但排序通常发生在大型数据集上,因此这个小的运行时成本变得更大。排序是模板与运行时多态对话的一个坏例子。
【解决方案2】:

“共同反应”是错误的。在“Effective C++”中,Scott Meyers 在第 41 条中说:

第 41 条:理解隐式接口和编译时多态性。

迈耶斯继续总结:

  • 类和模板都支持接口和多态性。
  • 对于类,接口是显式的并以功能为中心 签名。多态性通过虚函数在运行时发生。
  • 对于模板参数,接口是隐式的并且基于有效的 表达式。通过模板编译时发生多态性 实例化和函数重载决议。

【讨论】:

  • 他也打错了吗? :P
  • 我建议大家去读那一章,从介绍中会清楚你需要使用什么:)
  • 在 Effective C++ 第一版中,最相关的部分是第 42 条:“区分继承和模板”。那本书中的第 41 条是“明智地使用私有继承”。只需向拥有该版本的任何人提及这一点。
  • 这解释了它们是什么,但不能帮助您决定使用哪一个。
  • @MK。它至少告诉您,如果您需要动态多态性,则需要虚拟方法,而如果您只需要静态多态性(使编码更容易并编写通用代码而不是处理通用输入),那么模板/概念也是一种选择。
【解决方案3】:

当您想要保持类型安全或想要避免虚拟分派时,在基础(或组合)中使用模板。

【讨论】:

  • 确实,我怀疑面试官想要避免通过CRTP 进行虚拟调度。
【解决方案4】:

在定义适用于多种不相关对象的接口时,模板是合适的。模板对于容器类来说非常有意义,因为它需要泛化容器中的对象,同时保留类型信息。

在继承的情况下,所有参数必须是定义的参数类型,或者从它扩展。因此,当方法对正确具有直接层次关系的对象进行操作时,继承是最佳选择。

如果错误地应用了继承,则需要创建过于复杂的类层次结构,以及不相关的对象。代码的复杂性会增加一点点。如果是这种情况,请使用模板。

【讨论】:

    【解决方案5】:

    模板描述了一种算法,例如决定类的两个对象之间的比较结果或对它们进行排序。被操作的对象的类型(类)不同,但操作或逻辑或步骤等在逻辑上是相同的。

    另一方面,子类使用继承只是为了扩展或更具体的父类功能。 希望这是有道理的

    【讨论】:

      猜你喜欢
      • 2015-04-18
      • 2018-05-23
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-01-30
      相关资源
      最近更新 更多