Lua学习记录(6) --- Lua中的元表相关内容

目录

一、Lua中的元表的作用以及设计理念

二、关于元表的操作

(1)设置一个表为另一个表的元表

(2)__tostring方法的介绍

(3)__call方法的介绍

(4)一堆运算符的重载

[(5)__index的介绍 这个很重要](#(5)__index的介绍 这个很重要)

(6)__newindex的介绍

[(7)针对上面两种设计 还有反他们的设计 主要都是为了不时之需 那就是 rawget 和 rawset](#(7)针对上面两种设计 还有反他们的设计 主要都是为了不时之需 那就是 rawget 和 rawset)


一、Lua中的元表的作用以及设计理念

元表(metatable)是 Lua 里赋予表"扩展行为"的机制。因为 Lua 只有 table 这一种复合数据结构,官方希望在不引入复杂类型系统的前提下,也能做到运算符重载、自定义索引、对象模型等能力,所以设计了"把额外规则存进元表"的办法。任何 table 都可以附着一张元表;Lua 遇到无法直接处理的操作(例如对不存在的键取值、两个表相加、把 table 当函数调用)时,就去检查元表里对应的"元方法"(如 __index、__add、__call,这里具体的我们后面讲),如果存在就按你定义的逻辑来执行。

概念上,元表就是"描述某个 table 特殊行为的表"。它本身也是普通 table,只是被 Lua 引擎当作当前表的"说明书"。

其实元表也是一张表,当你对当前表的一些操作无法得到满足的时候 会去对你的元表有一定操作。

元表的初衷是:让简单的 table 也能拥有"像别的语言里类/对象那样的可定制行为",为 Lua 的灵活性和简洁语法提供底层支撑。

二、关于元表的操作

(1)设置一个表为另一个表的元表

很简单 申明两个表 然后用一个setmetatable(被设置表,元表)就完了

Lua 复制代码
-- 设置元表
meta = {}
myTable = {}
-- 设置元表函数
-- 第一个参数 子表 第二个参数 子表的元表
setmetatable(myTable, meta)

(2)__tostring方法的介绍

这个是什么呢?这个是 如果你有一个子表 你想对他进行打印操作 比如print(mytable) 如果有元表 那么就会执行 元表中的这个函数 不过这个函数默认是空的 需要我们自己写内容 比如:

不设置元表情况 设置元表情况

无参数情况 有参数情况

有一点需要注意的是 你可以将子表作为参数传进去

(3)__call方法的介绍

这个很简单 如果把子表当成函数调用时候 会调用执行这个call方法

这里解释一下第一个 true哪里来的 刚刚我们说了 如果设置了元表的子表有打印行为就会执行元表的__tostring()函数 这里就是这么来的

这个也是一样 默认传入的第一个参数就是子表

(4)一堆运算符的重载

不难 原理同上 下面是栗子🌰!值得关注的是 必须是同一元表 相加才有意义 不然没用还可能报错

Lua 复制代码
meta4 = {
    -- 相当于运算符重载 当子表使用+运算符时 会调用该方法
    -- 运算符+
    __add = function (t1,t2)
        return t1.age + t2.age
    end,
    -- 运算符-
    __sub = function (t1,t2)
        return t1.age - t2.age
    end,
    -- 运算符*
    __mul = function (t1,t2)
        return t1.age * t2.age
    end,
    -- 运算符/
    __div = function (t1,t2)
        return t1.age / t2.age
    end,
    -- 运算符%
    __mod = function (t1,t2)
        return t1.age % t2.age
    end,
    -- 运算符^
    __pow = function (t1,t2)
        return t1.age ^ t2.age
    end,
    -- 运算符==
    __eq = function (t1,t2)
        return t1.age == t2.age
    end,
    -- 运算符<
    __lt = function (t1,t2)
        return t1.age < t2.age
    end,
    -- 运算符<=
    __le = function (t1,t2)
        return t1.age <= t2.age
    end,
    -- 运算符..
    __concat = function (t1,t2)
        return tostring(t1.age)..tostring(t2.age)
    end
}
myTable4 = {
    age = 10
}
setmetatable(myTable4,meta4)

myTable5 = {
    age = 2
}
setmetatable(myTable5,meta4)
print(myTable4 + myTable5) -- 12
print(myTable4 - myTable5) -- 8
print(myTable4 * myTable5) -- 20
print(myTable4 / myTable5) -- 5
print(myTable4 % myTable5) -- 0
print(myTable4 ^ myTable5) -- 100
print(myTable4 == myTable5) -- false
print(myTable4 < myTable5) -- false
print(myTable4 <= myTable5) -- false
print(myTable4 .. myTable5) -- 102
-- -- 注意 ! 如果要用条件运算符 来比较两个对象
-- -- 那么这两个对象的原表一定要一致 才能准确调用重载的运算符 不然没用

(5)__index的介绍 这个很重要

这个的作用针对的是表中字段的查找操作

这个的作用就是 如果你在你当前表查找某个元素 而你这个表没有这个元素的话 那么就会到你的元表指定的__index的表进行元素的查找 及比如 子表A 的元表metaA的__index指向的表为 F。 F中有一个字段为 M。A表没有,如果我直接访问A.M那么这个肯定是nil的 ,如果我设置了A 的元表metaA的__index为F的话 那么就可以获取到这个字段了

记住啊 是到元表指定的表 而不是找不到元素来元表找

说白了 就是找不到东西会到原表指定表中继续查找 对于后面的面向对象中很有用

Lua 复制代码
-- __index的作用就是 当子表中找不到某个属性时候
-- 会到原表中__index指定的表中去寻找对应的属性
-- 如果找不到的话 就为空了
meta6 = {
    name = "元表的名字"
}
myTable6 = {
   -- name = "子表的名字"
}
-- 这个index一般写在表外面 内部可能会出现奇怪的问题 不知道有什么问题 我也看到过写里面的 
-- 这个index的查找可以一层层的往上找 一定是找__index指向的表
meta6.__index = {name = "12345"}
 setmetatable(myTable6,meta6)
print(myTable6.name)

(6)__newindex的介绍

这个的作用针对的是给表中字段赋值操作 如果给表中不存在字段赋值的话 会将这个字段转移到 你的元表的 __newindex指向的表中 创建这样一个字段然后赋值

触发时机:对表 t 写入一个目前不存在的 key 时(例如 t.x = 10 且 t.x == nil),Lua 会检查 t 的元表,如果定义了 __newindex 则改由它处理赋值

Lua 复制代码
-- newindex 当一个表中的属性赋值的时候 这个属性不存在的话
-- 那么会把这个值赋值到newindex所指的表中 不会修改自己
meta7 = {

}
meta7.__newindex = meta7
myTable7 = {

}
setmetatable(myTable7,meta7)
myTable7.age = 1000
print(myTable7.age)
print(meta7.age)

(7)针对上面两种设计 还有反他们的设计 主要都是为了不时之需 那就是 rawget 和 rawset

rawget 当我们使用这个函数时 会去找自己身上有没有这个变量 忽略index的设计

rawset 该方法会忽略newindex的设置 只会改自己的表中的变量

还有个小方法 获取自己的元表是谁 getmetatable(表)

写完收工 明日再见

相关推荐
报错小能手42 分钟前
C++流类库 标准输入流的安全性与成员函数 ostream 成员函数与自定义类型的IO
开发语言·c++·cocoa
VBA633742 分钟前
数组与字典解决方案第三十二讲:数组的拆分和维数转换
开发语言
进击的荆棘43 分钟前
C++起始之路——基础知识
开发语言·c++
The_cute_cat44 分钟前
FFmpeg的初步学习
学习·ffmpeg
郝学胜-神的一滴1 小时前
OpenGL错误检查与封装:构建健壮的图形渲染系统
开发语言·c++·程序人生·软件工程·图形渲染
繁华似锦respect1 小时前
C++ 设计模式之代理模式详细介绍
linux·开发语言·c++·windows·设计模式·代理模式·visual studio
Aevget1 小时前
界面控件DevExpress WPF v25.1新版亮点:富文本编辑器全新升级
开发语言·c#·wpf·devexpress·用户界面
芷栀夏1 小时前
多设备文件接力太麻烦?Go File + cpolar让传输效率翻倍
开发语言·后端·golang
松涛和鸣3 小时前
22、双向链表作业实现与GDB调试实战
c语言·开发语言·网络·数据结构·链表·排序算法