本文共 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 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!