• Lua元表和元方法


    Lua语言中的每个值都可以有元表(metatable) ,但只有full userdata和table才能有实例元表,而其他类型的值则共享对应类型所属的同一个元表。

    也就是说,可以为full userdata和table的实例单独设置不同的元表(TableUdata结构体上拥有struct Table *metatable成员变量)。

    在lua中只能给table设置元表,在c++中可以给所有类型设置元表。字符串标准库为所有string都设置了同一个元表,而其他类型默认是没有元表的。

    一个table还可以把自己设成自己的元表,用于描述其自身特有的行为。

    获取元表

    类型 lua示例 c++示例
    nil
    print(getmetatable(nil)) --nil
    lua_pushnil(L); // 将nil压入栈顶
    int ret1 = lua_getmetatable(L, -1); // nil无元表,返回值ret1为0
    boolean
    print(getmetatable(true)) --nil
    lua_pushboolean(L, 1); // 将boolean为true压入栈顶
    int ret1 = lua_getmetatable(L, -1); // boolean无元表,返回值ret1为0
    number
    print(getmetatable(3.14)) --nil
    print(getmetatable(100)) --nil
    lua_pushnumber(L, 3.14); // 将number(float)为3.14压入栈顶
    int ret1 = lua_getmetatable(L, -1); // number无元表,返回值ret1为0
    
    lua_pushinteger(L, 100); // 将number(int)为100压入栈顶
    int ret2 = lua_getmetatable(L, -1); // number无元表,返回值ret2为0
    string
    print(getmetatable("Hello World")) --table: 000001C47BA3FA80
    lua_pushstring(L, "Hello World"); // 将string为Hello World压入栈顶
    int ret1 = lua_getmetatable(L, -1); // string有元表,将元表压栈,返回值ret1为1
    if (lua_istable(L, - 1)) // true
    {
        const void* p = lua_topointer(L, -1); // 元表p为00000299AEDC6590
    }
    function
    print(getmetatable(print)) --nil
    lua_getglobal(L, "print"); // 获取全局函数print,并压入栈顶
    int ret1 = lua_getmetatable(L, -1); // fuction无元表,返回值ret1为0
    table
    print(getmetatable({})) --nil
    print(getmetatable(setmetatable({x=10},{}) )) --table: 000001C47D7F3820
    lua_newtable(L); // 创建一个空表,并压入栈顶
    int ret1 = lua_getmetatable(L, -1); // 空表无元表,返回值ret1为0
    
     
    
    lua_newtable(L); // 创建一个空表,并压入栈顶
    lua_pushstring(L, "x"); // 将string类型的键x压入栈顶
    lua_pushinteger(L, 10); // 将整型的值10压入栈顶
    lua_settable(L, -3); // 将表中的key为x的元素赋值为10,即:{x=10}。之后会将键和值都弹出栈
    lua_newtable(L); // 创建一个空表,并压入栈顶
    lua_setmetatable(L, -2);// 将刚创建的空表设置成索引为-2处的表的元表。之后将空表弹出栈
    int ret1 = lua_getmetatable(L, -1); // 栈顶的表有元表,将元表压栈,返回值ret1为1
    userdata
     
    print(getmetatable(io.stdin))  --table: 000001C47BA3FA00
    lua_getglobal(L, "io"); // 获取全局表io,并压入栈顶
    
    lua_pushstring(L, "stdin"); // 将string类型的键stdin压入栈顶
    lua_gettable(L, -2);  // 获取全局表io中键为stdio的userdata值,并压入栈顶
    int ret1 = lua_getmetatable(L, -1); // 类型为userdata的io.stdin有元表,将元表压栈,返回值ret1为1
    typedef struct Rect
    {
        float w, h;
    } Rect;
    
    Rect* ud1 = (Rect*)lua_newuserdata(L, sizeof(Rect)); //创建一个内存块为Rect的full userdata,并压入栈顶
    int ret1 = lua_getmetatable(L, -1); // 栈顶的full userdata没有元表,返回值ret1为0
    
     
    
    Rect* ud2 = (Rect*)lua_newuserdata(L, sizeof(Rect)); //创建一个内存块为Rect的full userdata,并压入栈顶
    lua_newtable(L); // 创建一个空表,并压入栈顶
    lua_setmetatable(L, -2);// 将刚创建的空表设置成索引为-2处的full userdata的元表。之后将空表弹出栈
    int ret2 = lua_getmetatable(L, -1); // 栈顶的full userdata有元表,将元表压栈,返回值ret2为1
    thread
    print(getmetatable(coroutine.create(function()  end)))  --nil
    lua_rawgeti(L, LUA_REGISTRYINDEX, LUA_RIDX_MAINTHREAD);
    int ret1 = lua_getmetatable(L, -1); // 栈顶的lua的mainthread没有元表,返回值ret1为0
    
     
    
    lua_newthread(L); // 创建一个thread,并压入栈顶
    int ret2 = lua_getmetatable(L, -1); // 栈顶的thread没有元表,返回值ret2为0

    基础类型的元表保存在global_State的struct Table *mt[LUA_NUMTAGS]数组中(宏LUA_NUMTAGS为9)

    string元表L->l_G->mt[LUA_TSTRING]中的内容如下:

    table: 00000299AEDC6590 {
      [__index] => table: 000001E3CC037D80 {
                     [gsub] => function: 00007FF779683FD0 {
                                 func: [C] -1
                               }
                     [dump] => function: 00007FF779683BF0 {
                                 func: [C] -1
                               }
                     [pack] => function: 00007FF779684A60 {
                                 func: [C] -1
                               }
                     [reverse] => function: 00007FF779683110 {
                                    func: [C] -1
                                  }
                     [len] => function: 00007FF779682EA0 {
                                func: [C] -1
                              }
                     [lower] => function: 00007FF779683270 {
                                  func: [C] -1
                                }
                     [match] => function: 00007FF779683DC0 {
                                  func: [C] -1
                                }
                     [gmatch] => function: 00007FF779683E30 {
                                   func: [C] -1
                                 }
                     [char] => function: 00007FF779683A70 {
                                 func: [C] -1
                               }
                     [find] => function: 00007FF779683D50 {
                                 func: [C] -1
                               }
                     [unpack] => function: 00007FF779685520 {
                                   func: [C] -1
                                 }
                     [format] => function: 00007FF7796843D0 {
                                   func: [C] -1
                                 }
                     [byte] => function: 00007FF779683830 {
                                 func: [C] -1
                               }
                     [rep] => function: 00007FF779683530 {
                                func: [C] -1
                              }
                     [packsize] => function: 00007FF779685340 {
                                     func: [C] -1
                                   }
                     [sub] => function: 00007FF779682F70 {
                                func: [C] -1
                              }
                     [upper] => function: 00007FF7796833D0 {
                                  func: [C] -1
                                }
                   }
    }

    ① L->l_G->mt中默认只有string类型有元表,其他类型都为空

    ② 在c++中可以重新为string设置新的元表

    ③ 在c++中可以为nil、boolean、light userdata、number、function和thread类型设置元表,设置后L->l_G->mt中对应的Table指针会被更新

    ④ table、full userdata的元表是设置在实例上的,因此,其对应的L->l_G->mt数组中的Table指针一直为空

    设置和取消元表

    类型 lua示例 c++示例
    table

    设置元表:

    local mt = {}
    
    local t1 = {x=10}
    
    setmetatable(t1, mt) -- 将mt表设置成t1表的元表
     
    -- 将mt表设置成{x=10}表的元表,并将结果赋值给t2表。等价于上面两句代码
    local t2= setmetatable({x=10},mt) 
     
    取消元表:
    setmetatable(t1, nil)  -- 取消t1表的元表

    设置元表:

    lua_newtable(L); // 创建一个空表a并压栈
    lua_pushstring(L, "x"); // 把字符串x压栈
    lua_pushinteger(L, 10); // 把整型数10压栈
    lua_rawset(L, -3); // 设置索引为-3处的表a的key(在栈顶下一个,即x)对应的value为10(在栈顶处)【不触发元方法】 设置成功后,将栈顶的2个元素都出栈
    lua_newtable(L); // 创建一个空表b并压栈
    lua_setmetatable(L, -2); // 将栈顶的空表b设置为索引为-2处表a的元表,并将栈顶的1个元素出栈

    取消元表:

    lua_pushnil(L);  // 把nil压栈
    lua_setmetatable(L, -2); // 将索引为-2处表的元表设置为nil(取消其元表),并将栈顶的1个元素出栈
    full userdata No

    设置元表:

    lua_newuserdata(L, 20); //创建一个内存块大小为20字节的full userdata,并压栈
    lua_newtable(L); // 创建一个空表a并压栈
    lua_setmetatable(L, -2); // 将栈顶的空表a设置为索引为-2处full userdata的元表,并将栈顶的1个元素出栈

    取消元表:

    lua_pushnil(L);  // 把nil压栈
    lua_setmetatable(L, -2); // 将索引为-2处full userdata的元表设置为nil(取消其元表),并将栈顶的1个元素出栈

    Lua语言中的每种类型的值都有一套可预见的操作集合。如:数字相加,字符串拼接等。但无法将table与其他类型相加。但可借助元表及其元方法,可以预先定义一系列操作的行为,如:运算符重载等。

    例如:当Lua试图将table与其他类型相加时,会先检查两者之一是否有元表及该元表是否有__add元方法(优先检查第1个加数的;第1个加数没有,才检查第2个加数的)。如果有,则调用找到的__add元方法来完成加法操作。

    local tb1 = {x=1}
    local mt1 = {}
    mt1.__add = function (a,b)
        return 10
    end
    
    -- print(tb1 + 1) -- Exception has occurred: attempt to perform arithmetic on a table value (local 'tb1')
    
    setmetatable(tb1,mt1)
    
    print(tb1 + 'hello') -- 输出10
    print(tb1 + {y=2}) -- 输出10
    print(3.2 + tb1) -- 输出10
    
    local tb2 = setmetatable({z=3}, {__add = function (a,b) return 20 end})
    print(tb1 + tb2) -- 输出10
    print(tb2 + tb1) -- 输出20

    注:无论调用的是tb1或者tb2元表中的__add__add的第一形参永远对应左操作数,第二形参永远对应右操作数。换言之,__add形参顺序与参与”+”运算的操作数顺序相同,而与使用左右操作数的哪一个元表中实现了__add无关。

    算术运算相关元方法

    元方法 说明 运算符类型
    __add(+) 加法 二元运算符:function(a, b)
    __sub(-) 减法 二元运算符:function(a, b)
    __mul(*) 乘法 二元运算符:function(a, b)
    __div(/) 除法 二元运算符:function(a, b)
    __idiv(//) floor除法(即:向下取整除法) 二元运算符:function(a, b)
    __mod(%) 取模 二元运算符:function(a, b)
    __pow(^) 幂运算 二元运算符:function(a, b)
    __unm(-) 取负数 一元运算符:function(a)
    __band(&) 按位与 二元运算符:function(a, b)
    __bor(|) 按位或 二元运算符:function(a, b)
    __bxor(~) 按位异或 二元运算符:function(a, b)
    __bnot(~) 按位取反 一元运算符:function(a)
    __shl(<<) 向左移位 二元运算符:function(a, b)
    __shr(>>) 向右移位 二元运算符:function(a, b)
    __concat(..) 连接运算 二元运算符:function(a, b)

    __add(+)代码示例

    local mt = {}
    mt.__add = function (a, b)
        local result = {}
        local size = math.max(#a, #b)
        for i=1, size do
            result[i] = (a[i] or 0) + (b[i]  or 0)
        end
        return result
    end
    
    local t1 = {10, 20, 30}
    setmetatable(t1, mt)
    
    local t2 = {2, 4}
    
    local t3 = t1 + t2  --t3 = {12, 24, 30}

    __unm(-)代码示例

    local function Unm(a)
        local result = {}
        for i=1, #a do
            result[i] = -1 * (a[i] or 0)
        end
        return result
    end
    
    local mt = {__unm = Unm}
    
    local t1 = {13, 20, 0, 30, 40}
    setmetatable(t1, mt)
    
    local t2 = -t1  --t2 = {-13, -20, 0, -30, -40}

    __band(&)代码示例

    local function Band(a, b)
        local result = {}
        local size = math.max(#a, #b)
        for i=1, size do
            result[i] = (a[i] or 0) & (b[i]  or 0)
        end
        return result
    end
    
    local mt = {__band = Band}
    
    local t1 = {10, 20, 0, 30, 40}
    setmetatable(t1, mt)
    
    local t2 = {2, 4, 6, 0}
    local t3 = t1 & t2 --t3 = {2, 4, 0, 0, 0}

    __bnot(~) 代码示例

    local function Bnot(a)
        local result = {}
        for i=1, #a do
            result[i] = ~(a[i] or 0)
        end
        return result
    end
    
    local mt = {__bnot = Bnot}
    
    local t1 = {13, 20, 0, 30, 40}
    setmetatable(t1, mt)
    
    local t2 = ~t1  --t2 = {-14, -21, -1, -31, -41}

    __shl(<<)代码示例

    local function Shl(a, b)
        local result = {}
        for i=1, #a do
            result[i] = (a[i] or 0) << b
        end
        return result
    end
    
    local mt = {__shl = Shl}
    
    local t1 = {2, 4, 6, 0}
    setmetatable(t1, mt)
    
    local t2 = t1 << 2 --t2 = {8, 16, 24, 0}

    __concat(..)代码示例

    local function Concat(a, b)
        local result = {}
        local size = math.max(#a, #b)
        for i=1, size do
            result[i] = math.tointeger((a[i] or 0) .. (b[i]  or 0))
        end
        return result
    end
    
    local mt = {__concat = Concat}
    
    local t1 = {10, 20, 0, 30, 40}
    setmetatable(t1, mt)
    
    local t2 = {2, 4, 6, 0}
    local t3 = t1 .. t2 --t3 = {102, 204, 6, 300, 400}

    关系运算相关元方法

    元方法 说明 型如
    __eq(==)

    是否相等 

    注:没有~=元方法,lua会将a~=b转换为not(a==b)

    二元运算符:function(a, b)
    __lt(<)

    是否小于 

    注:没有>元方法,lua会将a>b转换为b<a

    二元运算符:function(a, b)
    __le(<=)

    是否小于等于  

    注1:没有>=元方法,lua会将a>=b转换为b<=a

    注2:a<=b并不能转换为not(b<a)

             正常情况下,是可以转换的。但当a或b为nan时,则不成立。

             因为有nan数参与的比较,结果都是false

    local a = 0.0
    local b = 0.0
    local c = a / b  -- c为nan
    
    if c <= a then   -- 条件不满足
        print("c <= a")
    end
    
    if not (a < c) then
        print("not (a < c)")  -- 条件满足,打印出:not (a < c)
    end
    二元运算符:function(a, b)

    __le(<=)代码示例

    local function LessEqual(a, b)
        local size = math.max(#a, #b)
        for i=1, size do
            if (a[i] or 0) > (b[i] or 0) then
                return false
            end
        end
        return true
    end
    
    local mt = {__le = LessEqual}
    
    local t1 = {1, 4, 5, -1, 0}
    setmetatable(t1, mt)
    
    local t2 = {2, 4, 6, 0}
    local ret = t1 <= t2   --ret = true 


    表访问相关元方法

    元方法 说明 型如
    __index(读)

    访问表某个不存在的字段时,当该表设置了元表,将触发元表的__index元方法(若存在)调用并返回结果

    注1:访问表存在的字段,直接返回表中该字段的值,不会触发__index元方法

    注2:__index可以一个function(table, key),也可以为一个table。

             为function时,参数table为当前访问的表,key为当前访问的键

             为table时,可使用当前访问的键key,从该table中取得key对应的值

    注3:如果不想触发__index元方法来读取字段的值,lua中可使用rawget(table, key),c++中可使用int lua_rawget (lua_State *L, int index)

    function(table,key)
    table
    __newindex(写)

    设置表某个不存在的字段时,当该表设置了元表,将调用元表的__newindex元方法(若存在)来处理

    注1:设置表存在的字段,直接设置表中该字段的值,不会触发__newindex元方法

    注2:__newindex可以一个function(table, key, value),也可以为一个table。

             为function时,参数table为当前设置的表,key为当前设置的键, value为当前设置的值

             为table时,设置不存在的字段时,表自身并不会被赋值,而会对其元表的__newindex指向的表中该字段进行赋值

    注3:如果不想触发__newindex元方法来读取字段的值,lua中可使用rawset(table, key, value),c++中可使用void lua_rawset (lua_State *L, int index)

    function(table,key,value)
    table

    __index为function的代码示例

    local mt = {intdefault=100}
    mt.__index = function (t, k) -- t为当前访问的表,k为当前访问的键
        if math.type(k)=='integer' then -- k为整数时
            return mt.intdefault
        end
        return "lua"
    end
    
    local t = {1, 8, h1="earth", 5, h2="sun"}
    setmetatable(t, mt)
    
    
    print(t['h1']) -- earth
    print(t[3])    -- 5
    print(t.h2)    -- sun
    print(t[4])    -- 100
    print(t.h3)    -- lua

    __index为table的代码示例

    local base = {
        ["h1"] = "mars",
        [2] = 100,
        ["name"] = "chen"
    }
    local mt = {__index = base}
    
    local t = {1, 8, h1="earth", 5, h2="sun"}
    setmetatable(t, mt)
    
    
    print(t.h1)    -- earth
    print(t[1])    -- 1
    print(t.h2)    -- sun
    print(t[2])    -- 8
    print(t.name)  -- chen
    print(t.h3)    -- nil

    __newindex为function的代码示例

    local mt = {intdefault=100}
    mt.__newindex = function (t, k, v) -- t为当前设置的表,k为当前设置的键,v为当前设置的值
        if math.type(k)=='integer' then -- k为整数时
            return rawset(t, k, mt.intdefault) -- 不触发__newindex元方法来设置t[k]=mt.intdefault
        end
        return rawset(t, k, 'lua') -- 不触发__newindex元方法来设置t[k]='lua'
    end
    
    local t = {1, 8, h1="earth", 5, h2="sun"}
    setmetatable(t, mt)
    
    t['h1'] = "mars"
    print(t['h1']) -- mars
    t[3] = 10
    print(t[3])    -- 10
    t[4] = 20
    print(t[4])    -- 100
    t.h3 = "moon"
    print(t.h3)    -- lua

    __newindex为table的代码示例

    local base = {
        ["h1"] = "mars",
        [2] = 100,
        ["name"] = "chen"
    }
    local mt = {__newindex = base}
    
    local t = {1, 8, h1="earth", 5, h2="sun"}
    setmetatable(t, mt)
    
    
    t['h1'] = "moon"
    print(t['h1'], base["h1"]) -- moon    mars
    t[3] = 10
    print(t[3], base[3])       -- 10      nil
    t[4] = 20
    print(t[4], base[4])       -- nil     20     注:t[4]并没有赋值成20,而是其元表的__newindex指向的表的base[4]被赋值成20
    t.h3 = "sature"
    print(t.h3, base.h3)       -- nil     sature 注:t.h3并没有赋值成"sature",而是其元表的__newindex指向的表的base.h3被赋值成"sature"

    其他元方法

    元方法 说明 型如
    __tostring

    转换为字符串

    在lua中调用tostring(a)和print(a)函数,将触发a的元表的__tostring元方法调用

    1个参数:function(a)
    __len(#)

    取长度

    缺省情况下,对table取#,得到的是数组元素(不含末尾为nil的元素)的个数(不包含键值对)

    local t1 = {10,20,nil,30} -- #t1为4
    local t2 = {10,20,nil}  -- #t2为2
    local t3 = {nil,10,20}  -- #t3为3

    可以通过实现__len元方法,来重新定义#运算符的行为

    1个参数:function(a)
    __metatable 用于保护元表。getmetatable时返回__metatable的内容,setmetatable直接引发异常  
    __name 自定义对象的类型名称  
    __pairs 迭代器 2个参数:function(a, b)
    __call 实现talbe的()运算符。可以用来实现类似构造方法的调用形式。 N个参数:function(a, ...)
    __gc 该元方法称为终结器(finalizer)。用于垃圾回收该Lua对象之前,做一些额外的资源管理工作 (如:关闭文件、网络或数据库连接,或是释放一些自分配的内存)。 1个参数:function(a)
    __mode

    __mode用于设置表的key、value为弱引用(weak reference)。对应的表也被称为弱表(weak table)。

    当key、value为gc管理对象(function、table等),且只被弱引用所引用,则会被gc回收。

    3种弱引用的表:

    ① 具有弱引用key的table 

    local t={}
    setmetatable(t, {__mode="k"}) -- t为弱引用key的table

    ② 具有弱引用value的table

    local t={}
    setmetatable(t, {__mode="v"}) -- t为弱引用value的table

    ③ 同时具有弱引用key和弱引用value的table

    local t={}
    setmetatable(t, {__mode="kv"}) -- t为具有弱引用key和弱引用value的table

    任何情况下,只要key和value中的一个被gc,那么这个key-value对,就会被从表中移除

     

    __tostring代码示例

    local function ToString(a)
        local result = ""
        local size = #a
        for i=1, size do
            result = result .. (a[i] or 0)
            if i~=size then
                result = result .. ","
            end
        end
        return result
    end
    
    local mt = {__tostring = ToString}
    
    local t = {1, 4, 5, -1, 0}
    setmetatable(t, mt)
    
    print(t)  -- 打印出:1,4,5,-1,0
    local s = tostring(t)  -- s = '1,4,5,-1,0'

    __len(#)代码示例

    local function Length(a)
        local len = 0
        for _,_ in pairs(a) do
            len = len + 1
        end
        return len
    end
    
    local mt = {__len = Length}
    
    local t = {1, 8, h1=nil, h2="earth", nil, 5, h3="sun"}
    print(#t)  -- 打印出:4
    
    setmetatable(t, mt)
    print(#t)  -- 打印出:5

    __metatable代码示例

    local tb1 = {x=1}
    local mt1 = {}
    mt1.__metatable = "not your business."
    setmetatable(tb1, mt1)
    
    print(getmetatable(tb1)) -- not your business.
    -- setmetatable(tb1, {}) -- Exception has occurred: cannot change a protected metatable --引发错误

    __name代码示例

    local t =setmetatable({}, {__name = "MyObject"})
    -- 下面代码会导致异常。因为表t在其元表中没有实现__concat元方法
    local s = t.."go" --Exception has occurred: test.lua:5: attempt to concatenate a MyObject value (local 't')

    __pairs代码示例

    local default = {
        ["name"] = "chen",
        ["id"] = 7,
        ["age"] = 23,
        ["school"] = "whu"
    }
    
    local func = function (t, k)
        local nk, nv = next(default, k) -- 返回default表的下一个元素的键和值
        if nk and t[nk] then  -- 如果键不为nil,且在t表中存在
            nv = t[nk]        -- 将t表对应的值赋值给nv
        end
        return nk, nv
    end
    
    local test = {["id"] = 8}
    
    -- 以下for循环,打印出如下内容:
    --  id      8
    for k, v in pairs(test) do 
        print(k, v)
    end
    
    print("---------------------------")
    
    local mt = {}
    mt.__pairs = function (t, k)
       return func, t, nil
    end
    setmetatable(test, mt)
    
    -- 以下for循环,打印出如下内容:
    -- name    chen
    -- age     23
    -- id      8
    -- school  whu
    for k, v in pairs(test) do 
        print(k, v)
    end

    __call代码示例

    local t = setmetatable({x=100}, {__call = function (tb, ...)
        print("test __call! args:", tb, tb.x, ...)
    end})
    print(t) --table: 0000021A82720D60
    t(1,2,3,4,5) --test __call! args: table: 0000021A82720D60 100 1   2   3   4   5

    __gc代码示例

    local t = setmetatable({x=100}, {__gc = function (tb)
        print("test __gc! args:", tb, tb.x)
    end})
    print(t) --table: 000001BD61EA4A50
    t = nil
    -- 强制执行垃圾回收。会触发元方法__gc
    collectgarbage()  --test __gc! args: table: 000001BD61EA4A50 100

    __mode="k"代码示例

    a = {} -- a为一个全局表。不会被gc回收。
    
    local k1 = {}
    local v1 = {}
    a[k1] = v1
    
    local k2 = {}
    local k3 = {}
    local v2 = {}
    a[k2] = 100
    a[k3] = 200
    a[1] = v2
    a[2] = {}
    a[3] = function() end
    a[4] = "hello"
    a[5] = 300
    
    v1 = nil
    k2 = nil
    v2 = nil
    k3 = nil
    
    collectgarbage() -- 强制执行垃圾回收
    
    -- 以下for循环,打印出如下内容:
    -- 1       table: 000002381FB4B2B0
    -- 2       table: 000002381FB4B0B0
    -- 3       function: 000002381FAFB7C0
    -- 4       hello
    -- table: 000002381FB4A830 100
    -- table: 000002381FB4A6B0 table: 000002381FB49870
    -- 5       300
    -- table: 000002381FB4AFB0 200
    for i, v in pairs(a) do
        print(i, v)
    end
    
    print("---------------------------")
    
    setmetatable(a, {__mode = "k"}) -- 将key设置为弱引用
    collectgarbage() -- 强制执行垃圾回收
    
    -- 以下for循环,打印出如下内容:
    -- 1       table: 000002381FB4B2B0
    -- 2       table: 000002381FB4B0B0
    -- 3       function: 000002381FAFB7C0
    -- 4       hello
    -- table: 000002381FB4A6B0 table: 000002381FB49870
    -- 5       300
    for i, v in pairs(a) do
        print(i, v)
    end

    __mode="v"代码示例

    a = {} -- a为一个全局表。不会被gc回收。
    
    local k1 = {}
    local v1 = {}
    a[k1] = v1
    
    local k2 = {}
    local k3 = {}
    local v2 = {}
    a[k2] = 100
    a[k3] = 200
    a[1] = v2
    a[2] = {}
    a[3] = function() end
    a[4] = "hello"
    a[5] = 300
    
    v1 = nil
    k2 = nil
    v2 = nil
    k3 = nil
    
    collectgarbage() -- 强制执行垃圾回收
    
    -- 以下for循环,打印出如下内容:
    -- 1       table: 000002381FB4B2B0
    -- 2       table: 000002381FB4B0B0
    -- 3       function: 000002381FAFB7C0
    -- 4       hello
    -- table: 000002381FB4A830 100
    -- table: 000002381FB4A6B0 table: 000002381FB49870
    -- 5       300
    -- table: 000002381FB4AFB0 200
    for i, v in pairs(a) do
        print(i, v)
    end
    
    print("---------------------------")
    
    setmetatable(a, {__mode = "v"}) -- 将key设置为弱引用
    collectgarbage() -- 强制执行垃圾回收
    
    -- 以下for循环,打印出如下内容:
    -- 4       hello
    -- 5       300
    -- table: 000002381FB4A830 100
    -- table: 000002381FB4AFB0 200
    for i, v in pairs(a) do
        print(i, v)
    end

    __mode="kv"代码示例

    a = {} -- a为一个全局表。不会被gc回收。
    
    local k1 = {}
    local v1 = {}
    a[k1] = v1
    
    local k2 = {}
    local k3 = {}
    local v2 = {}
    a[k2] = 100
    a[k3] = 200
    a[1] = v2
    a[2] = {}
    a[3] = function() end
    a[4] = "hello"
    a[5] = 300
    
    v1 = nil
    k2 = nil
    v2 = nil
    k3 = nil
    
    collectgarbage() -- 强制执行垃圾回收
    
    -- 以下for循环,打印出如下内容:
    -- 1       table: 000002381FB4B2B0
    -- 2       table: 000002381FB4B0B0
    -- 3       function: 000002381FAFB7C0
    -- 4       hello
    -- table: 000002381FB4A830 100
    -- table: 000002381FB4A6B0 table: 000002381FB49870
    -- 5       300
    -- table: 000002381FB4AFB0 200
    for i, v in pairs(a) do
        print(i, v)
    end
    
    print("---------------------------")
    
    setmetatable(a, {__mode = "kv"}) -- 将key设置为弱引用
    collectgarbage() -- 强制执行垃圾回收
    
    -- 以下for循环,打印出如下内容:
    -- 4       hello
    -- 5       300
    for i, v in pairs(a) do
        print(i, v)
    end

    参考

    Lua 元表(Metatable)

  • 相关阅读:
    java回顾之多线程
    java回顾之异常
    模拟斗地主和冒泡排序
    java回顾之Map
    java回顾之集合List
    java回顾之树
    java回顾之单列集合、泛型、数据结构
    java回顾之类API再体验之引用类型小结
    java回顾之API初体验
    函数之 闭包函数 和 装饰器
  • 原文地址:https://www.cnblogs.com/kekec/p/14154340.html
Copyright © 2020-2023  润新知