lua中面向对象(class)实现探索(二)
来源:互联网 发布:mac和nars口红哪个滋润 编辑:程序博客网 时间:2024/05/17 06:33
说明:本文亦作为某章节出现在中山大学某实验室编撰的某教材中,本博客博主即该教程的编撰者,因此请不要因为看到本博客和该书中某章内容相同而认为这之间必有作假必有一方抄袭另一方。
云风的实现十分精妙但功能却有限,原因在于这样的实现无法做到一个功能,即在子类的函数中调用父类的同名函数(当然父类的同名函数中可能又调用了父类的父类的同名函数),你可能会说类不是保存了一个super指针吗?我不可以这样吗?
这样是行不通的,原因在于super变量保存在类中,而self只能访问到类的vtbl中的属性或方法。
你又或许会说,那我这样呢?
这样也是不行的,此时super访问到了,但是hello()函数访问不到,原因也很简单请自行分析。
为了解决上述的问题,我们对这份实现做出几个修改。
首先为了解决能在对象中通过self来访问super变量这个问题,我们将对class_type设置元表的操作提到class_type.super被定义赋值之前,也即这样做:
这样做的原因很简单,将对class_type设置元表的操作提前,那么之后在class_type中进行的定义super属性(也包括ctor和new属性)就会因为元表中的元方法而最后被在vtbl中创建出,从而实现了在对象中通过self来访问。
这样做了之后,通过self可以访问super 变量了,是否就意味着可以在子类函数中调用父类的同名函数了呢?比如这样:
看起来似乎没问题,实际上还是行不通的,问题在于” : ”(冒号)这个语法糖。 : 的作用在于它几乎以代码替换的方式将 : 左边紧接着的变量作为第一个参数传入了函数,因此self.super:hello()这句代码被lua虚拟机解释后的结果类似于下面这句:
self.super.hello(super )
看出问题来了吗?hello()函数这里原本希望接受的第一个参数应该是上述代码里的self(即实例化后的对象),也即上述代码原本期望被lua虚拟机解释的结果应该类似下面这句:
self.super.hello(self )
相对正确的做法是不利用语法糖,手动填写函数的第一个参数,也即直接写成期望被lua虚拟机解释后的样子:
self.super.hello(self )
至此,问题似乎得到了完美的解决,但真的如此吗?我们来考虑如下的情况,假设我们有这样的一份代码:
- --父类
- base = class()
- function base:ctor(val)
- print("base ctor")
- self._cnt = val or 0
- end
- function base:show()
- print("in baseshow")
- end
- --子类1
- child1 = class(base)
- function child1:ctor()
- print("child1 ctor")
- end
- function child1:show()
- self.super.show(self)
- print("in child1show ")
- end
- --子类2
- child2 = class(child1)
- function child2:ctor()
- print("child2 ctor")
- end
- function child2:show()
- self.super.show(self)
- print("in child2show")
- end
- function child2:create(...)
- local o = self.new(...)
- return o
- end
- o = child2:create()
- o:show()
我们满怀期望的运行,但结果是:
很悲伤吧,lua vm一点面子都不给。它不给面子的原因在于当前的class实现在多层继承中多层调用super方法时产生溢出,也即上述测试代码中的child1:show()的第一行调用super方法本意是想调用base类中的同名方法,可是此时的self.super仍然为child1。因此在child1:show()反复调用自身,陷入不可饶恕的自递归深渊最终栈溢出。
三、Lua中类支持调用super方法的实现
接着上文说,为了解决上面导致栈溢出的问题我们需要继续修改代码,在原有class函数中加入这么一段(这里只贴出代码段,最后会贴出最终的完整代码):
- class_type.super = function(self, f, ...)
- assert(self and self.__type == 'object', string.format("'self' must be a object when call super(self, '%s', ...)", tostring(f)))
- local originBase = self.__base
- --find the first f function that differ from self[f] in the inheritance chain
- local s = originBase
- local base = s.__base
- while base and s[f] == base[f] do
- s = base
- base = base.__base
- end
- assert(base and base[f], string.format("base class or function cannot be found when call .super(self, '%s', ...)", tostring(f)))
- --now base[f] is differ from self[f], but f in base also maybe inherited from base's baseClass
- while base.__base and base[f] == base.__base[f] do
- base = base.__base
- end
- -- If the base also has a baseclass, temporarily set :super to call that baseClass' methods
- -- this is to avoid stack overflow
- if base.__base then
- self.__base = base
- end
- --now, call the super function
- local result = base[f](self, ...)
- --set back
- if base.__base then
- self.__base = originBase
- end
- return result
- end
上述代码主要做了两个事:
1. 子类对象中调用super的f方法时,将沿着继承链表(super chain)自上而下在众父类中寻找第一个与自身f方法不同地址的类,那么这个类中的f方法就是需要寻找的。也即下面这段:
- while base.__base and base[f] == base.__base[f] do
- base = base.__base
- end
1. 找到同名不同地址的f方法后,判断当前类是否还有基类。如果有,则临时性的改变继承关系使得如果在该f方法中亦调用了super中的f方法,不至于栈溢出。
加入这个super函数后,子类对象中调用父类同名函数的方式改为:
self:super(‘funcName’, …)
也即之前的测试代码中的如下两个函数:
- function child1:show()
- self.super.show(self)
- print("in child1show ")
- end
- function child2:show()
- self.super.show(self)
- print("in child2show ")
- end
- function child1:show()
- self:super('show')
- print("in child1show ")
- end
- function child2:show()
- self:super('show')
- print("in child2show")
- end
没错,这正是我们想看到的。
最后贴一下完整的代码:
- -- Internal register
- local _class={}
- function class(base)
- local class_type={}
- class_type.__type = 'class'
- class_type.ctor = false
- local vtbl = {}
- _class[class_type] = vtbl
- setmetatable(class_type,{__newindex = vtbl, __index = vtbl})
- if base then
- setmetatable(vtbl,{__index=
- function(t,k)
- local ret=_class[base][k]
- vtbl[k]=ret
- return ret
- end
- })
- end
- class_type.__base = base
- class_type.new = function(...)
- --create a object, dependent on .__createFunc
- local obj= {}
- obj.__base = class_type
- obj.__type = 'object'
- do
- local create
- create = function(c, ...)
- if c.__base then
- create(c.__base, ...)
- end
- if c.ctor then
- c.ctor(obj, ...)
- end
- end
- create(class_type,...)
- end
- setmetatable(obj,{ __index = _class[class_type] })
- return obj
- end
- class_type.super = function(self, f, ...)
- assert(self and self.__type == 'object', string.format("'self' must be a object when call super(self, '%s', ...)", tostring(f)))
- local originBase = self.__base
- --find the first f function that differ from self[f] in the inheritance chain
- local s = originBase
- local base = s.__base
- while base and s[f] == base[f] do
- s = base
- base = base.__base
- end
- assert(base and base[f], string.format("base class or function cannot be found when call .super(self, '%s', ...)", tostring(f)))
- --now base[f] is differ from self[f], but f in base also maybe inherited from base's baseClass
- while base.__base and base[f] == base.__base[f] do
- base = base.__base
- end
- -- If the base also has a baseclass, temporarily set :super to call that baseClass' methods
- -- this is to avoid stack overflow
- if base.__base then
- self.__base = base
- end
- --now, call the super function
- local result = base[f](self, ...)
- --set back
- if base.__base then
- self.__base = originBase
- end
- return result
- end
- return class_type
- end
- lua中面向对象(class)实现探索(二)
- lua中面向对象(class)实现探索(二)
- lua中面向对象(class)实现探索(一)
- lua中面向对象(class)实现探索(一)
- Lua 中实现面向对象
- Lua 中实现面向对象
- Lua 中实现面向对象
- Lua 中实现面向对象
- Lua中实现面向对象
- LUA面向对象程序设计(二)继承
- lua面向对象实现的(记)
- 在 Lua 中实现面向对象
- Lua语言中面向对象的实现
- Lua中面向对象的实现
- 在 Lua 中实现面向对象
- Lua中 面向对象 概念和 class相关
- Lua 面向对象实现
- lua实现面向对象
- MATLAB使用集锦一
- PAT 甲级 1025. PAT Ranking (25)
- wordpress代码实现网站地图sitemap的html和xml的方法
- 20 多个可以提高你Android开发技能的国外优秀开源 app
- 搭建图片服务器《四》:后台java代码springMVC+spring实现图片上传
- lua中面向对象(class)实现探索(二)
- 关于AI算法芯片实现的几点看法(原创)
- HttpClient 实现 快递100 快递查询
- iOS dispatch_async到主线程封装C接口
- java之多线程(3)--线程池&Callable
- Permission denied (publickey)
- solr部署
- 华硕电脑的触摸板关闭
- 心中的几年后