lua面向对象

二、Lua中类的简单实现

         Lua的设计初衷并非意图构建完整的应用,而是嵌入在应用程序中为应用提供灵活的扩展和定制功能,所以Lua仅提供了基本的数学运算和字符串处理等函数库,而并未涵盖程序设计的方方面面。会让你惊讶的是,在面向对象概念已经泛滥的今天,lua作为新兴脚本语言其甚至没有原生态的提供对面向对象的支持,说简单点是lua没有class相关的关键字,其也不支持定义一个类,更别提多态了。

         不过读者肯定注意到了上面那句话中的“原生态”三个字,是的,原生态的lua中是没有类这个概念的。不过lua提供的table(表)这个强大的数据结构却赋予了程序员自行实现一个面向对象意义上的class的能力。

         废话休提,言归正传。先来看看coco2d-x 3.0所给出的官方的class的实现。

[plain]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. --Create an class.  
  2. function class(classname, super)  
  3.     local superType = type(super)  
  4.     local cls  
  5.   
  6.     if superType ~= "function" and superType ~= "table" then  
  7.         superType = nil  
  8.         super = nil  
  9.     end  
  10.   
  11.     if superType == "function" or (super and super.__ctype == 1) then  
  12.         -- inherited from native C++ Object  
  13.         cls = {}  
  14.   
  15.         if superType == "table" then  
  16.             -- copy fields from super  
  17.             for k,v in pairs(super) do cls[k] = v end  
  18.             cls.__create = super.__create  
  19.             cls.super    = super  
  20.         else  
  21.             cls.__create = super  
  22.         end  
  23.   
  24.         cls.ctor    = function() end  
  25.         cls.__cname = classname  
  26.         cls.__ctype = 1  
  27.   
  28.         function cls.new(...)  
  29.             local instance = cls.__create(...)  
  30.             -- copy fields from class to native object  
  31.             for k,v in pairs(cls) do instance[k] = v end  
  32.             instance.class = cls  
  33.             instance:ctor(...)  
  34.             return instance  
  35.         end  
  36.   
  37.     else  
  38.         -- inherited from Lua Object  
  39.         if super then  
  40.             cls = clone(super)  
  41.             cls.super = super  
  42.         else  
  43.             cls = {ctor = function() end}  
  44.         end  
  45.   
  46.         cls.__cname = classname  
  47.         cls.__ctype = 2 -- lua  
  48.         cls.__index = cls  
  49.   
  50.         function cls.new(...)  
  51.             local instance = setmetatable({}, cls)  
  52.             instance.class = cls  
  53.             instance:ctor(...)  
  54.             return instance  
  55.         end  
  56.     end  
  57.   
  58.     return cls  
  59. end  

这里不对此实现方式做过多讲解,重点将一些继承的实现。此class的实现中,子类继承父类时会将父类中所有的对象(变量或者函数)拷贝到子类中,也即这里做的:


从此子类和父类再无联系(除了子类的.super变量指向父类外),子类若调用继承自父类的函数实际调用的是从父类中一比一复制过来的函数。若子类中再自己覆盖了父类的同名函数,则之后在子类中再没有办法调用到子类继承自父类的此函数:

         你或许会说,难道我不可以这样吗?

[plain]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. --父类  
  2. Super = class("Super")  
  3. Super._cnt = 0  
  4.   
  5. function Super:show()  
  6.     self._cnt = self._cnt + 1  
  7.     print(string.format("cnt is %d", self._cnt))  
  8. end  
  9.   
  10. --子类  
  11. Child = class("Child", Super)  
  12.   
  13. function Child:show()  
  14.     --这样也不行吗  
  15.     self.super:show()  
  16.   
  17.     self._cnt = self._cnt + 1  
  18.     print(string.format("cnt is %d", self._cnt))  
  19. end  
  20.   
  21. function Child:create()  
  22.     local o = self.new()  
  23.     return o  
  24. end  
  25.   
  26. childObject1 = Child:create()  
  27. childObject2 = Child:create()  
  28.   
  29. childObject1:show()  
  30. childObject2:show()  

按照你的期待,上述代码的输出应该是:

cnt is 1

cnt is 2

cnt is 1

cnt is 2

或者你可能考虑到这里继承时将所有父类的对象拷贝到了子类中,因此子类对象中应该有一个自己的cnt和一个继承自父类的cnt,因此上述代码中self.super:show()中用到的self._cnt或许没有动态绑定到子类中的cnt,因此输出内容更应该是这样的:

cnt is 1

cnt is 1

cnt is 1

cnt is 1

可惜让你大跌眼镜的是,实际的输出内容却是这样的:


关于上述输出的原因,我这里留给读者自己去给出。这里简单的阐述一个概念,即lua中的table概念及其重要。上述类的实现导致类和类实例化出的对象其实都是table,而且它们的地位是平等的。也即对象和类在数据结构层次是一模一样的东西(也就是一个table),类是一个table,对象是一个table,类实例化生成对象时只是把自己内部的对象拷贝了一份到对象中。因此上述代码中childObejct1:show()和childObject2:show()函数体内的self.super:show()其实访问的是同一个函数,且该函数内的self._cnt也是同一个对象。

这种类实现简洁清晰却功能极其有限,基本体会不到类继承的意义,因此除非你的项目目标代码量不会超过1万行,否则就不要使用上述类的实现。

再来看看云风(吴云洋)大大在他的博客上给出的实现(什么你问我云风是谁?你你你真的是想做游戏的吗?),这里先直接贴出代码。

[plain]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. local _class={}  
  2. function class(super)  
  3.     local class_type={}  
  4.     class_type.ctor     = false  
  5.     class_type.super    = super  
  6.     class_type.new      =   
  7.         function(...)  
  8.             local obj={}  
  9.             do  
  10.                 local create  
  11.                 create = function(c,...)  
  12.                     if c.super then  
  13.                         create(c.super,...)  
  14.                     end  
  15.                     if c.ctor then  
  16.                         c.ctor(obj,...)  
  17.                     end  
  18.                 end  
  19.   
  20.                 create(class_type,...)  
  21.             end  
  22.             setmetatable(obj,{ __index = _class[class_type] })  
  23.             return obj  
  24.         end  
  25.     local vtbl={}  
  26.     _class[class_type]=vtbl  
  27.   
  28.     setmetatable(class_type,{__newindex=  
  29.         function(t,k,v)  
  30.             vtbl[k]=v  
  31.         end  
  32.     })  
  33.       
  34.     if super then  
  35.         setmetatable(vtbl,{__index=  
  36.             function(t,k)  
  37.                 local ret=_class[super][k]  
  38.                 vtbl[k]=ret  
  39.                 return ret  
  40.             end  
  41.         })  
  42.     end  
  43.   
  44.     return class_type  
  45. end  

博客中顺带给出了简单的使用范例:

[plain]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. base_type=class()       -- 定义一个基类 base_type  
  2. function base_type:ctor(x)  -- 定义 base_type 的构造函数  
  3.     print("base_type ctor")  
  4.     self.x=x  
  5. end  
  6. function base_type:print_x()    -- 定义一个成员函数 base_type:print_x  
  7.     print(self.x)  
  8. end  
  9. function base_type:hello()  -- 定义另一个成员函数 base_type:hello  
  10.     print("hello base_type")  
  11. end  
  12.   
  13. test=class(base_type)   -- 定义一个类 test 继承于 base_type  
  14. function test:ctor()    -- 定义 test 的构造函数  
  15.     print("test ctor")  
  16. end  
  17. function test:hello()   -- 重载 base_type:hello 为 test:hello  
  18.     --test.super:hello()  
  19.     print("hello test")  
  20. end  
  21.   
  22. a=test.new(2)   -- 输出两行,base_type ctor 和 test ctor 。这个对象被正确的构造了。  
  23. a:print_x() -- 输出 1 ,这个是基类 base_type 中的成员函数。  
  24. a:hello()   -- 输出 hello test ,这个函数被重载了。  

云风作为网易游戏技术总监,又同时是游戏技术界的传奇人物,他写出来的代码绝对让人叹服。这段代码有几处的设计极为巧妙,用了最少的代码实现了强大的功能同时代码结构还非常清晰干练,下面我们挑几处来分析:

首先看类的new函数,这里每定义一个class都会给它生成一个new函数,之后就可以通过className.new(…)来创建对象。


New函数里定义了一个递归函数create,该create判断传入的c(也即当前类)是否存在super(也即父类)如果存在则递归调用,这样一来就沿着类的递归链将类所有的父类自上而下传入create函数。而之后则调用其ctor(也即构造函数,如果存在)对对象进行构造,重点在这里,这里调用构造函数传入的第一个参数self是obj,也就是new函数第一句话申明出的局部对象。如此调用则在类(包含当前类及其所有父类,下同)的ctor中声明的变量通通都在obj中被创建,就成功的实现了将对象初始化(当然这也意味着不在类ctor中声明的变量不会在obj中被创建)。且在类ctor申明的变量的创建延迟到了对象被创建时因而这部分变量也不会在类中,相对那些现在类中创建变量之后实例化时把类中所有变量拷贝一份到实例化对象中的方法而言避免了一定的无意义内存开销。

函数的结尾是一句:

setmetatable(obj,{__index = _class[class_type] })

可能有部分读者会嘀咕,_class[class_type]是什么,我这里还看到相关代码啊?别急,下文马上就有了。


这里看到_class[class_type]其实指向了一个表vtbl(名字的命名来源于c++类对象中的虚表),该vtbl被设置为class_type的元表且对class_type的__newindex操作被hook到了在vtbl上进行。

         这里逻辑初步看起来不知其所以然,其真实目的是这样一做,之后在类(也就是这里即将被返回的class_type表)中添加的任何属性(变量)或方法(函数)都被实际在vtbl中创建,这时候回过头看看.new方法中的那句就瞬间明白了——这样一来就可以通过对象来访问到类中的方法了(当然也包括那些不在类的ctor中被申明的变量)。

         因此示例中test类在被class(base_type)创建出后添加的hello()方法,就能通过对象a:hello()来访问到。

         再来看看代码的最后一部分:


这里是用来实现类的继承逻辑的,test类继承自base_type类,test中的vtbl只保证了通过对象a能够访问到test中添加的方法,但是对于那些在test的父类base_type中的方法(比如例子中的print_x())就得靠这里来访问。这里给vtbl再设置了一个元表,其中__index原方法指向的就是父类的vtbl(这里保存有父类中的方法),因此最终的对象访问一个方法(比如print_x()),在其直接类(比如test)的vtbl中找不到时会向上到类的父类的vtbl中找,并如此递进直到找到了或者确定不存在为止。

         Vtbl[k]= ret这句是在第一次在父类中查找时把查找结果拷贝到当前类,从而避免了下一次访问的重复查找。

         另外需要注意的是,对于那些不在类的ctor()函数中申明的变量因为会保存在类的vtbl表中,该表对类唯一因而为类所有对象所共有。因此这种变量的性质有点类似c++中的静态变量。不过这里只能称之为”伪静态变量“,原因在于多层继承时,父类的这种变量在被最终对象第一次访问拷贝了一份,从而失去了全局唯一的性质。因此这里我们只能将它们称作“伪静态变量”。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值