模板和多态之间有什么区别

时间:2012-05-11 17:19:21

标签: c++ templates polymorphism

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

2 个答案:

答案 0 :(得分:14)

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

多态性的核心与派生类无关。多态性只是意味着能够在不了解它的情况下使用类型。多态性不是使用具体类型,而是依赖于某种形式的原型来定义它所采用的类型。任何适合该原型的类型都被接受。

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 ++无法拼写出特定模板函数/类放在某个类型上的原型,因此很容易意外地在模板类型上调用某些不应该的东西。当您尝试使用不适合原型的类型时,编译器将仅检测到此情况。即使这样,你通常会得到一个巨大的错误喷射(取决于你的模板代码的嵌套程度),这使得很难知道问题出在哪里。

实现隐式模板多态原型也要困难得多,因为它没有拼写出来。实现派生类需要遍历基类,查看所有虚函数并实现它们。为模板原型执行此操作要困难得多,除非有某些文档可以解释它。如果你没有实现某些东西,你会再次得到一个错误的喷射,通常比这个问题还要少。

答案 1 :(得分:0)

简而言之,这取决于您要解决的问题的共性和可变性分析的结果。

如果您对不同的内容执行相同的操作,则可以使用模板,例如2019-10-31 19:26:47.274 5407-5407/com.desotomatrix.digitalsignageplayer E/AndroidRuntime: FATAL EXCEPTION: main Process: com.desotomatrix.digitalsignageplayer, PID: 5407 java.lang.NullPointerException: Attempt to invoke virtual method 'void android.widget.ImageView.setImageResource(int)' on a null object reference at com.desotomatrix.digitalsignageplayer.Test$1.run(Test.java:30) at android.os.Handler.handleCallback(Handler.java:873) at android.os.Handler.dispatchMessage(Handler.java:99) at android.os.Looper.loop(Looper.java:209) at android.app.ActivityThread.main(ActivityThread.java:7021) at java.lang.reflect.Method.invoke(Native Method) at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:486) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:872) List<int>

如果您根据上下文对相同的内容以不同的方式进行相同的操作(这也具有一些共性),则可以使用多态,例如List<float>与派生词AbstractInterface::openFileWindowsInterface::openFile的接口,其中一个将根据上下文使用。抽象接口标识了在概念上执行相同操作,导数实现了概念实现的可变性这一事实的普遍性。