lua面向对象详解(2)

前面讲了lua的table,讲得比较粗略,不过table将会在不断的运用中逐渐形成对它的认识,不必一开始就完全搞明白它的全部。从这里开始,将讲解lua的元表和元方法。

二、元表与元方法

我们知道c++ 中不能随便将两个对象相加,除非程序用户自己定义+操作符,指定两个对象相加时需要做的操作。lua也是一样,不能将两个table进行算术操作,但是有一种方法可以实现。

元表和元方法就是用来改变lua 中元素的特定行为的。

lua中对元表的操作有如下方法:

setmetatable(t, mt)   设置 t 的元表为mt ,也可以重新设定

getmetatable(t) 获取 t 的元表,如果t 没有元表则返回nil

这里虽然说是可以对lua中元素的元表进行操作,但实际上,lua代码中只可以对table的元表进行设置,如要设置其他类型值的元表,则必须通过C API来实现。

lua中的每个值都有一个元表,table和userdata可以各自有独立的元表,而其他类型的值则共享该类型所属的单一元表。新建的table不会自动创建元表。

任何table都可以作为任何值的元表,而一组相关的table也可以共享一个通用的元表,此元表表示他们的共同行为。一个table甚至可以作为自己的元表,用于描述特有的行为。

下面以一个元素集合为例,说明元表与元方法的应用:

localSet = {}

functionSet.New(obj)

   obj = obj or {}

   local tt = {}

   for k,v in ipairs(obj) do

      tt[v] = true

   end

   return tt

end

 

functionSet.Union(obja , objb)

   local obj = Set.New()

   for k in pairs(obja) do

      obj[k] = true

   end

   for k in pairs(objb) do

      obj[k] = true

   end

   return obj

end

 

functionSet.Intersesion(obja , objb)

   local obj = Set.New()

   for k,v in pairs(obja) do

      obj[k] = objb[k]

   end

   return obj

end

 

functionSet.Print(obj)

   io.write("{ ")

   for k in pairs(obj) do

      io.write(k.." ")

   end

   print("}")

end

这里用Set表示一个集合的属性tabel,定义了一个New方法,将传进来的一组值表示成一个集合,这里用一个数组来表示,以该元素值为键,值为true表示存在该元素。

obj= obj or {} 是lua中比较常见的一种写法,用于让元素obj有一个不为nil的值,当用户调用函数没有传table进来时,我们获取的obj就为nil值,这是对obj进行索引迭代就会出错。所以obj不为nil时则为本身,否则为一个空table。

Set的Union函数为处理两个集合的并集,分别将两个集合的元素都按规则填充到集合中即可。Intersection函数则处理两个集合的交集,这里用了一点下技巧,先遍历obja集合,每个k都是集合obja的元素,执行操作

obj[k]= objb[k]

则当k为objb中的元素时,objb[k]为true,所以obj[k]= true将元素k加入到结果集。

当k不为objb中的元素时,objb[k]为nil,所以执行obj[k]= nil不会将k加入到结果集。

localt1 = Set.New{1,2,3,4,5,6}

localt2 = Set.New{4,5,6,7,8,9}

Set.Print(t1)

Set.Print(t2)

输出为

{ 1 2 3 4 5 6 }

{ 4 5 6 7 8 9 }

那么,我们要实现用操作符来求两个集合的并集和交集该怎么办呢?

定义一个用作元表的table

localmet = {}

在Set的New函数中增加

setmetatable(tt, met)

将新建的集合table的元表设置为met,并设置元表两个元方法如下

met.__add= Set.Union

met.__mul= Set.Intersesion

这里用算术运算+计算并集,用*计算交集,样就可以了:

localt3 = t1 + t2

Set.Print(t3)

localt4 = t1 * t2

Set.Print(t4)

输出为

{1 2 3 4 5 6 7 8 9 }

{5 6 4 }

可以看到,t3确实是t1和t2的并集,t4确实是t1和t2的交集。

那么,table的元表又是如何运作的呢?

当lua解析器解析t1 + t2时,如果t1有元表,并且元表中有__add字段,则用t1元表的该元方法,而与t2的元表无关,只有当t1没有找到这种元方法是才会查找t2(注意如果t2的元表的__add元方法与t1不同的情形)。当t1和t2中都找不到__add元方法时,lua就会引发一个错误。

通过这个例子,我们想一下元表和元方法是什么?

我们可以把元表看成事本例中Set集合对象抽象出来的一个类对象(注意不是类,元表是table也是对象),这个对象定义了对于以它为元表的对象某些操作的行为(后面还会讲到,元对象提供的不单是方法,还有值)。

lua中只有对象,没有类,对象可以生成对象,再次强调这句话。

当对table进行索引某个方法或值时,lua首先会查找table本身是否有这样的方法或值,如果有则直接使用。否则,会查找该table是否存在元表,并在查找的元表中查找这样的方法或值。

lua中还定义了一些其他的具有通用操作符的元方法:

算术:__add(加法)、__sub(减法)、__mul(乘法)、__div(除法)、__unm(相反数)、__mod(取模)、__pow(乘幂)、__concat(字符串连接)

关系:__eq(等于)、__lt(小于)、__le(小于等于)

其他几个重要的元方法:

__tostring 可以定义将table表示为字符串的元方法

__metatable 调用setmetatable和getmetatable会用到元表的该元方法。如果手动设置元表的__metatable这个元方法的值,则会对table起到保护的作用,外部将不能访问该table的元方法,更不能调用setmetatable设置新元表。

localtt = {}

localmt = {}

mt.__metatable= "not your business"

setmetatable(tt, mt)

print(getmetatable(tt))

setmetatable(tt, {})

输出为

notyour business

lua:metatable2.lua:6: cannot change a protected metatable

__index做为lua中一个非常重要的元方法,它可以是一个函数也可以是一个table。当它为一个函数时,lua会以table和一个不存在不存在的key(存储的key则直接访问)来调用函数。如果为一个table,lua就以相同的方式来重新访问这个table。什么意思呢?来看下面的一个例子:

localwindow = {}

window.prototype= {x=10,y=20,width=10,height=10}

window.mt= {}

window.mt.__index= function(tbl , key)

   return window.prototype[key]

end

functionwindow.New(obj)

   obj = obj or {}

   setmetatable(obj , window.mt)

   return obj

end

localtb = window.New{x=20,y=30}

print(tb.x)

print(tb.width)

window表将一些属性存储在prototype中,还有一个作为元表的mt。当调用New函数生成对象tb时,设置了tb的元表为mt。当对tb索引x时,首先查找tb本身是否存在x键,发现存在则直接访问。索引width时,tb没有,lua发现它有元表,就跳转到表mt中,要访问mt必须找到__index元方法,发现是一个函数,则将tb和width作为参数传入调用。

那__index为table时又是什么情况呢,我们来做一个实验:

localwindow = {}

window.prototype= {x=10,y=20,width=20,height=20}

window.mt= {}

functionwindow.New(obj)

   obj = obj or {}

   setmetatable(obj , window.mt)

   return obj

end

localt = window.New{x=20,y=30}

print(t.width)

这里会输出什么呢?结果是nil。对t索引width时虽然会跳转到元表window.mt上,但是lua并不知如何处理元表window.mt(缺少__index元方法)。

当我们在window.mt = {}之后加上一行:

window.mt.__index= window.prototype

输出就是20了。

也就是说,元方法__index是告诉lua对元表应该如果访问或操作。前面的例子将mt的__index元方法指向一个函数,则lua会调用这个函数。这里将mt的__index指向一个table,则lua就会去访问这个table。

当然,这种访问还会引起其他的一下操作,我们将在面向对象的时候将到。


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值