原始实现
在functions.lua
函数281行开始 给出了class
的实现方式
1 |
|
测试
将如上函数保存为ClassQ.lua
再创建如下文件
Animal.lua
1 | AnimalClass = class("AnimalClass") |
Cat.lua
1 | CatClass = class("CatClass",AnimalClass) |
入口文件main.lua
1 | require("ClassQ") |
输出结果为:
1 | call animal ctor name is: A1 |
分析
删减
QuickX
有一部分是为了要考虑直接和引擎C++层绑定,这部分不在我们的讨论范围中. 所以将其class函数中相关部分代码去掉.
最终代码为:
1 | function class(classname, super) |
等效转换
接着和之前一样 我们再把这部分代码 改些名字,去除个语法糖 等效 的改成如下代码:
1 | function class(classname, super) |
拆解分析
事先了解
如果你对为何能等效转换及2B情景法有疑问,请看我之前的文章: Lua从入门到入了门(2) 基础难点概述
new 函数
首先要拆开了来看, 先看new函数部分
1 |
|
这部分很简答, 就是把小弟和大哥关联了一下. 顺便调用了一下小弟的ctor
函数 让其初始化以下.
class 定义部分
比较绕的是class定义部分
1 |
|
AnimalClass
定义小a的大哥AnimalClass
时候,大哥就是一个含有ctor函数的空表,注意此时AnimalClass
还不是小a的大哥,只是一张普通的table而已. 所以
1 | newClass.__index = newClass |
这行定义 只是找了一个名字叫__index
的Key,让其值又引用了其自身, 只有当创建小a时候
1 | a1 = AnimalClass.new("A1") |
此时AnimalClass
才成了小a的大哥,AnimalClass.__index=AnimalClass
才等效与之前例子中的GreenPaper
(在之前的教程中)
CatClass
CatClass
有别与AnimalClass
,定义时候是有super的. 所以就是相当于 设置了 大哥的大哥.
1 |
|
所以当小c(c1) 调用某个函数时候 首先问其大哥CatClass
,
c1其实只包含了数据,都是 通过
self.mColor=xxx
这种在某些函数中定义的,自身是不包含任何函数的. 所以外面一调用c1:SayHI()
这种函数c1
中肯定是没有的,所以就会去向其大哥求救
大哥查了一下自己发现自己也搞不定
大哥之所以会查自己,是因为上面
newClass.__index = newClass
设置的,大哥自己能搞得定的情况就是说明子类Override了父类的方法了.也就是大哥中有这个实现. 比如例子中的CatClass:SayHI
. 没有实现的 比如CatClass:Eat
就是大哥自己也搞不定的情况了.
大哥(CatClass)搞不定就去问了其大哥的大哥AnimalClass
CatClass
会去问AnimalClass
是因为设置了setmetatable(newClass, {__index = super})
, 这部分有点绕 涉及到点有几个
CatClass
是c1的元表,但是其自身也是表,他也是可以有元表的
CatClass
的元表 并不是AnimalClass
😎 惊不惊喜?意不意外? 详情可以看我之前的文章, 就是真大哥,假大哥那块.
CatClass
只有自己查不到的情况下才会向AnimalClass
求救的. 这也是我之前一直比较懵圈的地方. 用2B情景法来描述就是小弟(c1)先自己解决,解决不了找大哥(CatClass)
小弟(CatClass)先自己解决,解决不了找大哥(AnimalClass)这样一层一层就递归下去了
小结
Quick这个实现版本基本上来说已经够用了,不过调用时候有几个点要注意
new函数需要用 .
的方式来调用,其他函数却需要用:
来调用
1 |
|
原因是因为其new函数声明时候用的点,内部instance用的是冒号,这样用冒号去调用new函数时候会多往ctor中传一个”self”(这个self其实是Class)
1 |
|
可以实现子类重载后还能调用父类的方法只不过实现起来看着会比较诡异
比如Cat
中的SayHI函数
1 | CatClass.super.SayHI(self, _msg) |
理解起来其实倒是不难, CatClass.super
也就是 AnimalClass
也就变成了
AnimalClass.SayHI(self, _msg)
此时self是c1
, 不能直接 AnimalClass:SayHI(_msg)
,因为这样就没把c1
传过去. 相当于表儿传错了