lua中面向对象(class)
Lua中类的简单实现

     Lua的设计初衷并非意图构建完整的应用,而是嵌入在应用程序中为应用提供灵活的扩展和定制功能,所以Lua仅提供了基本的数学运算和字符串处理等函数库,而并未涵盖程序设计的方方面面。会让你惊讶的是,在面向对象概念已经泛滥的今天,lua作为新兴脚本语言其甚至没有原生态的提供对面向对象的支持,说简单点是lua没有class相关的关键字,其也不支持定义一个类,更别提多态了。不过读者肯定注意到了上面那句话中的“原生态”三个字,是的,原生态的lua中是没有类这个概念的。不过lua提供的table(表)这个强大的数据结构却赋予了程序员自行实现一个面向对象意义上的class的能力。废话休提,言归正传。先来看看coco2d-x 3.0所给出的官方的class的实现。

–Create an class.
function class(classname, super)
local superType = type(super)
local cls

if superType ~= "function" and superType ~= "table" thensuperType = nilsuper = nil
endif superType == "function" or (super and super.__ctype == 1) then-- inherited from native C++ Objectcls = {}if superType == "table" then-- copy fields from superfor k,v in pairs(super) do cls[k] = v endcls.__create = super.__createcls.super    = superelsecls.__create = superendcls.ctor    = function() endcls.__cname = classnamecls.__ctype = 1function cls.new(...)local instance = cls.__create(...)-- copy fields from class to native objectfor k,v in pairs(cls) do instance[k] = v endinstance.class = clsinstance:ctor(...)return instanceendelse-- inherited from Lua Objectif super thencls = clone(super)cls.super = superelsecls = {ctor = function() end}endcls.__cname = classnamecls.__ctype = 2 -- luacls.__index = clsfunction cls.new(...)local instance = setmetatable({}, cls)instance.class = clsinstance:ctor(...)return instanceend
endreturn cls

end

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

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

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

–父类
Super = class(“Super”)
Super._cnt = 0

function Super:show()
self._cnt = self._cnt + 1
print(string.format(“cnt is %d”, self._cnt))
end

–子类
Child = class(“Child”, Super)

function Child:show()
–这样也不行吗
self.super:show()

self._cnt = self._cnt + 1
print(string.format("cnt is %d", self._cnt))

end

function Child:create()
local o = self.new()
return o
end

childObject1 = Child:create()
childObject2 = Child:create()

childObject1:show()
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万行,否则就不要使用上述类的实现。

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

local _class={}
function class(super)
local class_type={}
class_type.ctor = false
class_type.super = super
class_type.new =
function(…)
local obj={}
do
local create
create = function(c,…)
if c.super then
create(c.super,…)
end
if c.ctor then
c.ctor(obj,…)
end
end

            create(class_type,...)endsetmetatable(obj,{ __index = _class[class_type] })return objend
local vtbl={}
_class[class_type]=vtblsetmetatable(class_type,{__newindex=function(t,k,v)vtbl[k]=vend
})if super thensetmetatable(vtbl,{__index=function(t,k)local ret=_class[super][k]vtbl[k]=retreturn retend})
endreturn class_type

end

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

base_type=class() – 定义一个基类 base_type
function base_type:ctor(x) – 定义 base_type 的构造函数
print(“base_type ctor”)
self.x=x
end
function base_type:print_x() – 定义一个成员函数 base_type:print_x
print(self.x)
end
function base_type:hello() – 定义另一个成员函数 base_type:hello
print(“hello base_type”)
end

test=class(base_type) – 定义一个类 test 继承于 base_type
function test:ctor() – 定义 test 的构造函数
print(“test ctor”)
end
function test:hello() – 重载 base_type:hello 为 test:hello
–test.super:hello()
print(“hello test”)
end

a=test.new(2) – 输出两行,base_type ctor 和 test ctor 。这个对象被正确的构造了。
a:print_x() – 输出 1 ,这个是基类 base_type 中的成员函数。
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++中的静态变量。不过这里只能称之为”伪静态变量“,原因在于多层继承时,父类的这种变量在被最终对象第一次访问拷贝了一份,从而失去了全局唯一的性质。因此这里我们只能将它们称作“伪静态变量”。

原文:https://blog.csdn.net/mywcyfl/article/details/37706085

lua中面向对象(class)相关推荐

  1. lua中面向对象(class)实现探索(一)(转)

    转自:https://blog.csdn.net/mywcyfl/article/details/37706085 说明:本文亦作为某章节出现在中山大学某实验室编撰的某教材中,本博客博主即该教程的编撰 ...

  2. lua中面向对象(class)实现探索(一)

    说明:本文亦作为某章节出现在中山大学某实验室编撰的某教材中,本博客博主即该教程的编撰者,因此请不要因为看到本博客和该书中某章内容相同而认为这之间必有作假必有一方抄袭另一方. 二.Lua中类的简单实现 ...

  3. Lua中的userdata

    userdata 是一种用户自定义数据,用于表示一种由应用程序或 C/C++ 语言库所创建的类型,可以将任意 C/C++ 的任意数据类型的数据(通常是 struct .指针和类)存储到 Lua 变量中 ...

  4. Cocos2d-x 脚本语言Lua中的面向对象

    Cocos2d-x 脚本语言Lua中的面向对象 面向对象不是针对某一门语言,而是一种思想.在面向过程的语言也能够使用面向对象的思想来进行编程. 在Lua中,并没有面向对象的概念存在,没有类的定义和子类 ...

  5. Lua中的面向对象实现探讨

    Lua中,面向对向是用元表这种机制来实现的.元表是个很"道家"的机制,很深遂,很强大,里面有一些基本概念比较难理解透彻.不过,只有完全理解了元表,才能对Lua的面向对象使用自如,才 ...

  6. Lua——Lua中的面向对象

    开始 Lua本身并不是面向对象的语言.不存在类的概念.Lua官网16.1 – Classes中有如下描述. Lua does not have the concept of class 但我们可以在L ...

  7. Lua中的模块与module函数详解

    很快就要开始介绍Lua里的"面向对象"了,在此之前,我们先来了解一下Lua的模块. 1.编写一个简单的模块 Lua的模块是什么东西呢?通常我们可以理解为是一个table,这个tab ...

  8. lua的面向对象编程,封装,继承,多态的实现

    简介 1. lua面向对象编程是基于元表metatable,元方法__index来实现的,具体元表和元方法的介绍 请见Lua的元表metatable及元方法 2. 语法糖 语法糖是由英国计算机科学家彼 ...

  9. lua中给userdata绑定元表示例

    前言 这篇博客,我估计写不好.一方面是内容挺绕,一方面是我没有看过书,是照葫芦画瓢写代码. 前置要求: Lua调用C代码 lua中表与元表 Lua操作C语言用户自定义类型数据Userdata 上面第三 ...

最新文章

  1. R语言使用pwr包的pwr.r.test函数对相关信息分析进行效用分析(power analysis)、在已知效应量(effect size)、显著性水平、效用值的情况下计算需要的样本量
  2. html字体效果标签,纯CSS模拟fieldset标签效果把文字写在边框上
  3. JavaScript实现Apache .htaccess 转化nginx生成器工具-toolfk程序员工具网
  4. jQuery基础:下(事件及动画效果)
  5. 解决在IOS系统及微信中audio、video不能自动播放的问题
  6. ios下微信标题修改
  7. mysql data_add data_sub
  8. 用 Go 语言,做 Web 编程开发
  9. ubuntu查看gpu使用率_如何监控GPU卡的使用率(Linux)
  10. 【HDU - 3440】House Man(差分约束)
  11. 【5G落地】首批5G商用牌照正式颁发!5G和AI并肩前行,会带来下一次的工业革命吗?...
  12. oracle 值安全性,Oracle Solaris 11 安全性預設值
  13. python imagedraw line_修复PIL.ImageDraw.Draw.宽线条线条线条
  14. access html导出,AccessToFile
  15. 软考 | 软考高项论文该如何去写?
  16. Java代码实现SM2算法以及注意点总结(踩坑记录)
  17. mysql如何清空数据库表内容
  18. c语言爱心函数3D,C语言控制台打印3D爱心图案
  19. 【python】实用tools
  20. VirtualBox虚拟机E_INVALIDARG (0x80070057)

热门文章

  1. jvax.net.ssl.SSLException: 异常解决
  2. MIC编程(1)——MIC是什么?
  3. Xqk.Data数据框架开发指南:丰富的、灵活的查询方法(第三部分:SqlField)
  4. 活性染料研究:Lumiprobe AF594 NHS 酯,5-异构体
  5. python画圣诞老人简笔画_利用Python绘制有趣的万圣节南瓜怪效果
  6. 分布式文件系统之GPFS
  7. Java JDK 下载安装,以及环境配置
  8. 论文笔记-基于BiLSTM 模型的漏洞检测
  9. 个体户报税流程如何?需要交哪些税
  10. 炉石传说 android手机版本区别,《炉石传说》手机版上架iPhone和安卓平台