[TOC]

基本

1. 概念

定义:Lua 中的每个值都可以用一个 metatable。 这个 metatable 就是一个原始的 Lua table , 它用来定义原始值在特定操作下的行为。 你可以通过在 metatable 中的特定域设一些值来改变拥有这个 metatable 的值 的指定操作之行为。[1]

2. 例子

看一个例子就能够很好理解metatable的用法了

-- 两种写法都可以^_^
local t1 = {20, 1, key1 = "hello", key2 = "world", lang = "lua"}
local t2 = {
key1 = "hello new",
key2 = "world new",
}
print("Before setmetatable: ")
print("a2 metable = ", getmetatable(t2))
print("language =", t2["lang"])

– 使用用setmetatable设置元表
setmetatable(t2, {__index = t1})
print("After setmetatable:")
print("a2 metable =", getmetatable(t2))
print ("language =", t2["lang"])

结果为:

Before setmetatable: 
a2 metable = 	nil
language =	nil

After setmetatable:
a2 metable = table: 004CCC28
language = lua

理解

1. 元表和元方法关系

元表和元方法的关系是:如定义中所说,需要通过元表中特定域做一些指定操作之行为。

什么意思呢?也就是元表触发了某个事件(特定域)的时候,会通过这个事件绑定的操作(元方法)来做特定事情。举个例子:当一个值(lua中的每个值都可以用一个metatable,只是table用的比较多)是非数字,却要加法操作的时候,Lua 会检查它的 metatable 中 “__add” 域中的是否有一个函数(元方法),然后做特定的事情。

2. 操作

lua规定操作名字加上两个下划线 ‘’ 前缀的字符串,所以这里也可以知道自定义名字的时候最好不要用’’, 下面是metatable可以控制的操作:

  • “add”: + 操作。
  • “sub”: - 操作。 其行为类似于 “add” 操作。
  • “mul”: * 操作。 其行为类似于 “add” 操作。
  • “div”: / 操作。 其行为类似于 “add” 操作。
  • “mod”: % 操作。 其行为类似于 “add” 操作, 它的原生操作是这样的 o1 - floor(o1/o2)*o2
  • “pow”: ^ (幂)操作。 其行为类似于 “add” 操作, 它的原生操作是调用 pow 函数(通过 C math 库)。
  • “unm”: 一元 - 操作。
  • “concat”: .. (连接)操作。
  • “len”: # 操作。
  • “eq”: == 操作。 函数 getcomphandler 定义了 Lua 怎样选择一个处理器来作比较操作。 元方法仅仅在参于比较的两个对象类型相同且有对应操作相同的元方法时才起效。
  • “lt”: < 操作。
  • “le”: <= 操作。
  • “index”: 取下标操作用于访问 table[key] 。
  • “newindex”: 赋值给指定下标 table[key] = value 。
  • “call”: 当 Lua 调用一个值时调用。

3. 注意

  • 每个 table 和 userdata 拥有独立的 metatable (当然多个 table 和 userdata 可以共享一个相同的表作它们的 metatable); 其它所有类型的值,每种类型都分别共享唯一的一个 metatable。

  • 设置元表的时候,如果表是一个空表,没有指定一些操作,其实没有什么意义

应用

  1. 复制表函数copy

代码:

--@function: 复制一个table
--@tbOrg: 源table
function copy(tbOrg)
	-- 注意:放置互引用情况下的死循环
	local tbSaveExitTable = {}
	local function _copy(object)
       	if type(object) ~= "table" then
            return object;
        elseif tbSaveExitTable[object] then	--检查是否有循环嵌套的table
            return tbSaveExitTable[object];
        end
        local tbNewTable = {};
        tbSaveExitTable[object] = tbNewTable;
        for index, value in pairs(object) do
            tbNewTable[_copy(index)] = _copy(value);	
        end
		-- 使用:保持元表一致
        return setmetatable(tbNewTable, getmetatable(object));
    end
	return _copy(tbOrg);
end

参考

[1]http://manual.luaer.cn/2.8.html