模板和多态性有什么区别

what is the difference between templates and polymorphism

本文关键字:什么 区别 多态性      更新时间:2023-10-16

大家好,我对模板和多态性有疑问。根据定义,多态性提供了代码可重用性,模板在某种意义上允许用户通过提供具有不同数据类型的泛型编程来使用相同的代码。那么使用多态性而不是模板有什么好处。这可能是一个愚蠢的问题,但我很好奇确切的区别。

你似乎误解了多态性是什么。

多态性的核心与派生类无关。多态性只是意味着在不知道所有内容的情况下使用类型的能力。多态性不是使用具体类型,而是依赖于某种形式的原型来定义它需要的类型。接受适合该原型的任何类型。

C++,运行时多态性是通过从包含虚函数的基类派生类来提供的。基类和虚函数构成了多态原型。为接受调用这些虚函数的基类而编写的代码将接受从基类派生的任何类实例。

编译时多态性是发生的多态性...在编译时;)这意味着编译器必须知道发生了什么。您可能已经针对多态原型编写了C++代码,但编译器并不关心。编译后您将获得特定的具体类型。

编译时多态性由 C++ 中的模板提供。模板函数或类可以采用符合原型的任何类型,通常称为"概念"。与基类和虚函数不同,原型是隐式的:原型仅由模板函数/类如何使用类型来定义。

如果您有此模板函数:

template<typename T>
void Stuff(T &t)
{
  t.call(15);
}

T有一个隐含的要求。这个要求是它有一个名为 call 的成员函数。此成员函数必须有一个可以使用整数值调用的重载。

这意味着可以使用任何恰好适合此原型的类型。

模板多态性比继承多态性更广泛,因为它可以由更广泛的类型使用。必须专门设计类型以使用继承多态性;你必须从类派生。类型可以是非破坏性的(即:您不必更改类型本身(适应模板多态性。如果您的模板原型设计精良,则更是如此:

template<typename T>
void Stuff(T &t)
{
  call(t, 15);
}

此版本的Stuff所需要的只是有一些函数采用T&和整数值。如果我有某种类型要与Stuff一起使用,我所要做的就是在适当的命名空间(即定义类型的命名空间(中定义一个call函数。这将正常工作。所有这些都无需修改类型本身。

当然,编译时多态性是...编译时。如果我希望一些用户输入或数据文件选择多态类型,模板不会有很大帮助(尽管基于模板的技术类型擦除可以提供帮助(。运行时多态性的主要优点是它确实是运行时。

另一个好处是它的原型更精确。关于继承,一切都明确说明。基类中的虚函数接口布局清晰。编译器将阻止您尝试错误地使用该基类(调用其上不存在的方法(。事实上,一个体面的 IDE 将指导您的代码,以便您只看到基类上的方法。

模板多态性要隐式得多。由于C++无法拼写出特定模板函数/类放在类型上的原型,因此很容易意外地在模板类型上调用不应该调用的内容。仅当您尝试使用不适合原型的类型时,编译器才会检测到此问题。即便如此,您通常也会喷出大量错误(取决于模板代码的嵌套深度(,这使得很难知道问题出在哪里。

实现隐式模板多态原型也困难得多,因为它没有详细说明。实现派生类需要遍历基类,查看所有虚函数并实现它们。为模板原型执行此操作要困难得多,除非在某处有文档详细说明。如果你没有实现某些东西,你会再次得到一个错误,这个错误通常不是关于这个问题的。

简而言之,它归结为您对要解决的问题的共性和可变性分析的结果。

如果你对不同的东西做同样的操作,那么你会使用模板,例如 List<int>List<float>

如果你根据上下文以不同的方式对相同的东西执行相同的操作,这也有一些共性,那么你将使用多态性,例如AbstractInterface::openFile接口与导数WindowsInterface::openFileLinuxInterface::openFile,其中一个将根据上下文使用。 抽象接口标识了以下事实的共性:在概念上执行相同的操作,导数满足概念实现的可变性。