metatable.元方法 = function (可接受参数)
(函数体)
end
```
#### table访问的元方法
**元方法:**
算数运算符:`__add`(加法)[+]、`__mul`(乘法)[*]、`__sub`(减法)[-]、`__div`(除法)[/]、`__unm`(相反数)[-]、`__mod`(取模)[%]、`__pow`(乘幂)[^]。
逻辑运算符:`__eq`(等于)[=]、`__lt`(小于)[<]、`__le`(小于等于)[<=]。
其他运算符:`__concat`(连接)[..]、`__len`(取长度)[#]。
其他元方法:
`__tostring`:返回值(可接受参数:table)
` __call`:函数调用(可接受参数:table, key)
`__metatable`:保护元方法(字符串)
`__index`:查找表索引(可接受参数:table, key)
`__newindex`:添加新索引(可接受参数:table, key, value)
**说明:**
**`__add`**
如果任何不是数字的值(包括不能转换为数字的字符串)做加法, Lua 就会尝试调用元方法。 首先、Lua 检查第一个操作数(即使它是合法的), 如果这个操作数没有为 "__add" 事件定义元方法, Lua 就会接着检查第二个操作数。 一旦 Lua 找到了元方法, 它将把两个操作数作为参数传入元方法, 元方法的结果(调整为单个值)作为这个操作的结果。 如果找不到元方法,将抛出一个错误。(其它 算数运算符和逻辑运算符及其它运算符跟这个类似)
**`__index`**
当我们访问一个表的不存在的域,返回结果为nil,这是正确的,但并不一定正确。实际上,这种访问触发lua解释器去查找__index元方法:如果不存在,返回结果为nil;如果存在则由__index 元方法返回结果。当我们想不通过调用__index 元方法来访问一个表,我们可以使用rawget函数。Rawget(t,i)的调用以raw access方式访问表。这种访问方式不会使你的代码变快(the overhead of a function call kills any gain you could have),但有些时候我们需要他,在后面我们将会看到。
**`__newindex`**
__newindex元方法用来对表更新,__index则用来对表访问。当你给表的一个缺少的域赋值,解释器就会查找__newindex元方法:如果存在则调用这个函数而不进行赋值操作。像__index一样,如果元方法是一个表,解释器对指定的那个表,而不是原始的表进行赋值操作。另外,有一个raw函数可以绕过元方法:调用rawset(t,k,v)不掉用任何元方法对表t的k域赋值为v。__index和__newindex 元方法的混合使用提供了强大的结构:从只读表到面向对象编程的带有继承默认值的表。
>参考文章
>[Lua 元表与元方法](https://blog.csdn.net/jingangxin666/article/details/80382748 )
>[lua元表以及元方法](https://www.cnblogs.com/Dong-Forward/p/6063365.html)
## 0X02 lua类的实现
lua类的实现直接上类的实现的代码,注释加好了,文件名 `class.lua`
```js
--class.lua
local _class = { }
function class(super)
-- 一个类的构建,这里构建的是类本身,ctor是构造函数,super是父类,这里继承只允许一个父类
local class_type = { ctor = false, super = super }
--vtbl是当前类中所有域存放的地方
local vtbl = { }
_class[class_type] = vtbl
--_class[super]这里返回的是super 本身作为class_type 对应的vtbl
--父类的vtbl
vtbl.super = _class[super]
class_type.superclass = _class[super]
--设置class_type类本身的元方法,这里操作的是vtbl,并没有修改class_type本身(查找域和添加域都是操作的vtbl,class_type只是简单的原型)
setmetatable(class_type, {
__newindex = function(t, k, v) vtbl[k] = v end,
__index = function(t, k) return vtbl[k] end,
} )
if super then
--关联父类子类的关系的查找域,vtbl关联父类的btbl查找域
setmetatable(vtbl, {
__index =
function(t, k)
if k and _class[super] then
local ret = _class[super][k]
vtbl[k] = ret
return ret
else
return nil
end
end
} )
end
class_type.New = function(...)
-- 一个类实例的构建
local obj = { class = class_type }
-- 设置实例关联类的查找域vtbl
setmetatable(obj, {
__index =
function(t, k)
return _class[class_type][k]
end
} )
--类和所有父类的ctor构造函数收集,第一个当前类的ctor,第二个父类的ctor,第三个父类的父类的ctor,....
local inherit_list = { }
local class_ptr = class_type
while class_ptr do
if class_ptr.ctor then table.insert(inherit_list, class_ptr) end
class_ptr = class_ptr.super
end
local inherit_length = #inherit_list
--调用所有构造函数,从最上层的父类ctor开始知道当前类的ctor
if inherit_length > 0 then
for i = inherit_length, 1, -1 do
inherit_list[i].ctor(obj, ...)
end
end
obj.super = inherit_list[2];
if detectMemoryLeak then
registerToWeakTable(obj, debug.traceback("obj lua stack:"));
end
obj.class = class_type
return obj
end
class_type.is = function(self_ptr, compare_class)
if not compare_class or not self_ptr then return false end
local raw_class = self_ptr.class
while raw_class do
if raw_class == compare_class then return true end
raw_class = raw_class.super
end
return false
end
return class_type
end
--测试代码
function printclass(class_type)
local s="{"
for k,v in pairs(class_type)do
s=s..tostring(k)..":"..tostring(v)..","
end
s=s.."}"..tostring(class_type)
print("class_type::"..s);
s="{"
local vtbl=_class[class_type]
for k,v in pairs(vtbl)do
s=s..tostring(k)..":"..tostring(v)..","
end
s=s.."}"..tostring(vtbl)
print("vtbl::"..s);
end