lua中metatable学习
发布日期:2021-06-30 19:38:44 浏览次数:2 分类:技术文章

本文共 4314 字,大约阅读时间需要 14 分钟。

Lua 中 metatable 是一个普通的 table,但其主要有以下几个功能:

1.定义算术操作符和关系操作符的行为

2.为 Lua 函数库提供支持
3.控制对 table 的访问

Metatables 定义操作符行为

Metatable 能够被用于定义算术操作符和关系操作符的行为。例如:Lua 尝试对两个 table 进行加操作时,它会按顺序检查这两个 table 中是否有一个存在 metatable 并且这个 metatable 是否存在 __add 域,如果 Lua 检查到了这个 __add 域,那么会调用它,这个域被叫做 metamethod。

Lua 中每个 value 都可以有一个 metatable(在 Lua 5.0 只有 table 和 userdata 能够存在 metatable)。每个 table 和 userdata value 都有一个属于自己的 metatable,而其他每种类型的所有 value 共享一个属于本类型的 metatable。在 Lua 代码中,通过调用 setmetatable 来设置且只能设置 table 的 metatable,在 C/C++ 中调用 Lua C API 则可以设置所有 value 的 metatable。默认的情况下,string 类型有自己的 metatable,而其他类型则没有:

print(getmetatable('hi')) --> table: 003C86B8print(getmetatable(10))  --> nil

为 Lua 函数库提供支持

Lua 库可以定义和使用的 metamethod 来完成一些特定的操作,一个典型的例子是 Lua Base 库中 tostring 函数(print 函数会调用此函数进行输出)会检查并调用 __tostring metamethod:

local mt = {}mt.__tostring = function(t)    return '{' .. table.concat(t, ', ') .. '}'end local t = {1, 2, 3}print(t)setmetatable(t, mt)print(t)

metatable可以类似c++中重载操作符(重写元方法)

f1 = { a = 1, b = 2 }  f2 = { a = 2 , b = 3 }    -- s = f1 + f2    现在的f1 和 f2 没有各自的__add函数  meta = {}    function meta.__add(num1, num2)      local sum = {}      sum.a = num1.a + num2.a       sum.b = num1.b + num2.b       return sum   end    setmetatable(f1, meta)  setmetatable(f2, meta)    s = f1 + f2   print(s.a)  print(s.b)
需要重载的操作符  
算数类型  
 __add(a, b)                     for a + b  
 __sub(a, b)                     for a - b  
 __mul(a, b)                     for a * b  
 __div(a, b)                     for a / b  
 __mod(a, b)                     for a % b  
 __pow(a, b)                     for a ^ b  
 __unm(a)                        for -a  
 __concat(a, b)                  for a .. b  
 __len(a)                        for #a  
关系类型  
 __eq(a, b)                      for a == b  
 __lt(a, b)                      for a < b  
 __le(a, b)                      for a <= b  
table访问的元方法  
 __index(a, b)  <fn or a table>  for a.b  
 __newindex(a,b)  
 __newindex(a, b, c)             for a.b = c  
 __call(a, ...)                  for a(...)  
 库定义的元方法  
 __metatable                      保护元表,不可读写  
 __tostring  

-- metatable 可以当成一个类来使用Parent = {}    function Parent:new()      local newParent = { house = "white house" }      self.__index = self       return setmetatable(newParent, self)  end    function Parent:Wife( )      print("mother live in the "..self.house )  end  function Parent:SayHello( )      print("hello")  end   parent = Parent:new()  parent:Wife()  --通过类似类的方式来继承Child = Parent:new()  function Child:Position()      local ChildHouse = self.house      print("child live in the "..ChildHouse)      self:Wife()     self:SayHello() end child = Child:new()  child:Position()

__index这个重载,主要是重载了find key的操作。这操作可以让Lua变得有点面向对象的感觉,让其有点像Javascript的prototype。

所谓__index,说得明确一点,如果我们有两个对象a和b,我们想让b作为a的prototype只需要:

setmetatable(a, {__index = b})

例如下面的示例:你可以用一个Window_Prototype的模板加上__index的MetaMethod来创建另一个实例:

Window_Prototype = {x=0, y=0, width=100, height=100}MyWin = {title="Hello"}setmetatable(MyWin, {__index = Window_Prototype})

于是:MyWin中就可以访问x, y, width, height的东东了。(注:当表要索引一个值时如table[key], Lua会首先在table本身中查找key的值, 如果没有并且这个table存在一个带有__index属性的Metatable, 则Lua会按照__index所定义的函数逻辑查找)

有了以上的基础,我们可以来说说所谓的Lua的面向对象。

Person={} function Person:new(p)    local obj = p    if (obj == nil) then        obj = {name="ChenHao", age=37, handsome=true}    end    self.__index = self    return setmetatable(obj, self)end function Person:toString()    return self.name .." : ".. self.age .." : ".. (self.handsome and "handsome" or"ugly")end

上面我们可以看到有一个new方法和一个toString的方法。其中:

1)self 就是 Person,Person:new(p),相当于Person.new(self, p)
2)new方法的self.__index = self 的意图是怕self被扩展后改写,所以,让其保持原样
3)setmetatable这个函数返回的是第一个参数的值。

于是:我们可以这样调用:

me = Person:new()print(me:toString()) kf = Person:new{name="King's fucking", age=70, handsome=false}print(kf:toString())

继承如下,Lua和Javascript很相似,都是在Prototype的实例上改过来改过去的。

Student = Person:new() function Student:new()    newObj = {year = 2013}    self.__index = self    return setmetatable(newObj, self)end function Student:toString()    return "Student : ".. self.year.." : " .. self.nameend
例子讲解:
Role = { hp = 100 }function Role:new(o)    o = o or {}      setmetatable(o, self)    self.__index = self    return oend
当执行 "r = Role:new() " 创建一个对象时,r 将 Role 设置为自己的元表,那么调用 "r:addHp(50)" 的时候,会在 r 里查找 addHp 方法,如果没有找到,则会进一步搜索其元表的 __index,因此等价于:
getmetatable(r).__index.addHp(r, 50)
从上面的 Role:new 方法可以知道,Role 的 __index 在创建时被指定为 self,因此其实就是执行:
Role.addHp(R, 50)
完整例子:
Role = { hp = 100 }function Role:new(o)    o = o or {}      setmetatable(o, self)    self.__index = self    return oendfunction Role:addHp(hp)    self.hp = self.hp + hpend r = Role:new()r:addHp(50)print(r.hp)

转载地址:https://linxinfa.blog.csdn.net/article/details/51854924 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!

上一篇:vs2010字体和颜色的舒适设置
下一篇:Lua 中实现面向对象

发表评论

最新留言

网站不错 人气很旺了 加油
[***.192.178.218]2024年04月23日 02时30分16秒