ad

《自己动手写 Python 虚拟机》_更理解虚拟机的意义_2.4.3 访问者模式

网友投稿 126 2023-11-07

【摘要】 本书摘自《自己动手写 Python 虚拟机》一书中第2章,第4节,海纳编著。

2.4.3 访问者模式

在面向对象程序设计中,经常会遇到有相同模式的问题。后来人们把相似的问 题归纳总结,提出了一整套解决方案,这个解决方案就是设计模式。充分利用设计模 式,会极大地提高程序的可读性和可维护性。在编译器设计领域,也会使用很多经典 的设计模式。但对于本书里的内容,最重要的两个设计模式是访问者模式和单例 模式。

访问者模式用于访问抽象语法树是非常合适的,另外一个非常适用的地方是自 动内存管理。由于本书选择了使用C++ 来实现 Python 虚拟机,因此这里就选择使 用C++ 来讲解访问者模式。在学习访问者模式的同时,也复习一下C++ 的对象内 存模型,因为这将决定如何实现虚拟机中的类、对象以及内存管理。

1. 静态类型

这个程序的运行结果是,输出三个“emm …” 。 这个结果可能会出乎部分读者 的意料。因为d、c、f分别指向的实例是 Dog 、Cat 和Fox, 但是当 Speaker 调用它们 的 speak 方法时,却调用了Animal 类的方法。这是因为d、c、f 虽然在运行中各指向

不同的对象,但它们的静态类型却都是“Animal*” 。 而 C++ 在编译期就会决定静态类型变量的方法调用,这种绑定方法的方式叫静态绑定。

《自己动手写 Python 虚拟机》_更理解虚拟机的意义_2.4.3 访问者模式

2. 动态类型

有什么办法可以让程序根据运行时的动态类型,来调用相应类型的 speak 方 法呢?

答案就是关键字 virtual, 也就是C++ 的虚函数机制。为父类中的 speak 方法添 加 virtual, 将其变为虚函数,试着运行一下观察结果。代码如下:

只修改父类,子类完全不修改,编译执行,可以看到这一次的结果是 wang,mi- ao,woo 。 可见,通过把父类中的 speak 方法定义为虚函数即可使得程序在运行时, 根据对象的实际类型分别调用不同的方法。那么,虚函数的机制到底是怎么实现的 呢?这里有一个更直接的例子

第20行代码的运行结果是100。而sizeof(Base) 的结果是8,sizeof(Derive) 的结 果是12。类定义中出现了虚函数,类的大小就增加了4,这是因为类对象中多了一个 被称为虚函数表指针的内容。为什么要加这个指针呢?原因在于,对于虚函数,编译 器无法像对待普通成员函数那样,在编译阶段通过指针的类型就能判断调用的是 Base::func 还是 Derive::func 。 对虚函数的调用,必须通过该指针所指向的对象是 Base 类的对象,还是 Derive 类的对象来确定。如果指针 pBase 所指的对象是 Base 的实例,那么 pBase>func 就是 Base::func, 而如果指针 pBase 所指的对象是 Derive 的实例,那么 pBase→func 就是 Derive::func。

如果 Base 还有其他子类,pBase 同样可以指向这些子类的实例而不发生编译错 误。可以看出,让编译器在编译阶段就确定调用哪个函数是不可能的;只能在程序运 行的时候,动态地确定要调用的是哪个函数。C++ 提供了一种叫做虚函数表的机制 来解决这个问题。

如图2.10所示,如果一个类的定义中包含了虚函数,那么这个类的对象就会多 一个指针项。为了查找的效率,这个指针项会出现在对象的开始部分,这个指针被称 为虚表指针。虚表指针指向一个表,这个表就是虚表(Virtual Table)。虚表里的每 一项也是一个指针,更准确地说,是指向函数的指针,分别指向实际应该调用的函数。

版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们 18664393530@aliyun.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:《Python+3自动化软件发布系统》Django 2实战_了解Python的更好方法_3.5.3 Salt-API配置
下一篇:《Python学习笔记 从入门到实战》_更了解Python的途径之一_9.3 命名空间
相关文章

 发表评论

暂时没有评论,来抢沙发吧~

×