对码当歌,猿生几何?

c++编译期多态与运行期多态,显示接口和隐式接口

运行期多态(runtime polymorphism)

运行期多态的设计思想要归结到类继承体系的设计上去。对于有相关功能的对象集合,我们总希望能够抽象出它们共有的功能集合,在基类中将这些功能声明为虚接口(虚函数),然后由子类继承基类去重写这些虚接口,以实现子类特有的具体功能。典型地我们会举下面这个例子:
image.png

image.png

image.png运行期多态的实现依赖于虚函数机制。当某个类声明了虚函数时,编译器将为该类对象安插一个虚函数表指针,并为该类设置一张唯一的虚函数表,虚函数表中存放的是该类虚函数地址。运行期间通过虚函数表指针与虚函数表去确定该类虚函数的真正实现。
运行期多态的优势还在于它使处理异质对象集合称为可能:
image.png

总结:运行期多态通过虚函数发生于运行期

编译期多态(compile-time polymorphism)

对模板参数而言,多态是通过模板具现化和函数重载解析实现的。以不同的模板参数具现化导致调用不同的函数,这就是所谓的编译期多态。
相比较于运行期多态,实现编译期多态的类之间并不需要成为一个继承体系,它们之间可以没有什么关系,但约束是它们都有相同的隐式接口。我们将上面的例子改写为:

image.png

image.png

在编译之前,函数模板中t.shout()调用的是哪个接口并不确定。在编译期间,编译器推断出模板参数,因此确定调用的shout是哪个具体类型的接口。不同的推断结果调用不同的函数,这就是编译器多态运行期多态与编译期多态优缺点分析

运行期多态优点

OO设计中重要的特性,对客观世界直觉认识。
能够处理同一个继承体系下的异质类集合。

运行期多态缺点

运行期间进行虚函数绑定,提高了程序运行开销。
庞大的类继承层次,对接口的修改易影响类继承层次。
由于虚函数在运行期在确定,所以编译器无法对虚函数进行优化。
虚表指针增大了对象体积,类也多了一张虚函数表,当然,这是理所应当值得付出的资源消耗,列为缺点有点勉强。

编译期多态优点

它带来了泛型编程的概念,使得C++拥有泛型编程与STL这样的强大武器。
在编译器完成多态,提高运行期效率。
具有很强的适配性与松耦合性,对于特殊类型可由模板偏特化、全特化来处理。

编译期多态缺点

程序可读性降低,代码调试带来困难。
无法实现模板的分离编译,当工程很大时,编译时间不可小觑。
无法处理异质对象集合。。这类似于重载函数在编译器进行推导,以确定哪一个函数被调用。

关于显式接口(explicit interfaces)与隐式接口(implicit interfaces)

通常显式接口由函数的签名式(也就是函数名称、参数类型、返回类型)构成
所谓的显式接口是指类继承层次中定义的接口或是某个具体类提供的接口,总而言之,我们能够在源代码中找到这个接口.显式接口以函数签名为中心,例如:
我们称shout为一个显式接口。在运行期多态中的接口皆为显式接口
image.png

隐式接口就完全不同了。它并不基于函数签名式,而是由有效表达式(valid expressions) 组成
例如:
image.png

对于anim来说,必须支持哪一种接口,要由模板参数执行于anim身上的操作来决定,在上面这个例子中,T必须支持shout()操作,那么shout就是T的一个隐式接口。