让我举一个例子说明这些差异,因为它们并不相同。是的,当您通过超类引用对象时,动态分派可以让您选择正确的方法,但是这种魔术是特定于该类层次结构的,您必须在基类中进行一些声明才能使其起作用(抽象方法填写vtable,因为表中方法的索引不能在特定类型之间改变)。因此,您可以通过通用的Cat指针在Tabby,Lion和Tiger中调用所有方法,甚至可以将填充有Lions,Tigers和Tabbys的Cat数组。它知道这些方法在编译时(静态/早期绑定)在对象的vtable中引用了哪些索引,即使该方法是在运行时选择的(动态分配)也是如此。
现在,让我们实现一个包含狮子,老虎和熊的数组!((天啊!))。假设在C ++中没有名为Animal的基类,您将需要做大量工作,因为在没有通用基类的情况下,编译器将不允许您进行任何动态分配。vtable的索引需要匹配,而这不能在未发布的类之间完成。您需要有一个足够大的vtable来容纳系统中所有类的虚拟方法。C ++程序员很少将此视为限制,因为您已经受过训练,可以思考有关类设计的某种方式。我并不是说它的好坏。
通过后期绑定,运行时无需通用的基类即可解决此问题。通常有一个哈希表系统,用于在类中查找方法,而调度程序中使用的是缓存系统。在C ++中,编译器知道所有类型。在后期绑定语言中,对象本身知道其类型(不是无类型的,大多数情况下对象本身都知道它们是谁)。这意味着我可以根据需要拥有多种类型的对象的数组(狮子,老虎和熊)。而且,您可以实现消息转发和原型制作(允许在不更改类的情况下更改每个对象的行为)和各种其他方式,与不支持后期绑定的语言相比,这种方式更加灵活并且可以减少代码开销。
曾经在Android中编程并使用findViewById()吗?您几乎总是最终将结果转换为正确的类型,而转换基本上是编译器的谎言,并放弃了所有应该使静态语言更优越的静态类型检查优势。当然,您可以改为使用findTextViewById(),findEditTextById()以及其他一百万个,以便您的返回类型匹配,但是这将多态性抛在了窗外。可以说是OOP的全部基础。后期绑定的语言可能会让您简单地按ID索引,并将其视为哈希表,而不管正在索引或返回什么类型。
这是另一个例子。假设您拥有Lion类,并且它的默认行为是在看到它时就吃饭。在C ++中,如果您想拥有一头“经过训练的”狮子,则需要创建一个新的子类。原型制作使您可以简单地更改需要更改的特定Lion的一种或两种方法。它的类和类型不变。C ++无法做到这一点。这很重要,因为当您有一个继承自Lion的新“ AfricanSpottedLion”时,您也可以对其进行培训。原型不会改变类的结构,因此可以进行扩展。通常,这是这些语言处理通常需要多重继承的问题的方式,或者也许多重继承是您处理缺乏原型的方式。
仅供参考,Objective-C是C,其中添加了SmallTalk的消息传递功能,而SmallTalk是原始的OOP,并且两者均受以上所有功能的约束。从微观级别的角度来看,后期绑定语言可能会稍微慢一些,但通常可以使代码以在宏级别上更高效的方式进行结构化,并且都归结为优先选择。