Lua_Metatable元表

本文详细探讨了Lua中元表的使用与功能,包括元表的设置与获取、元表关键字如__metatable,__index,__newindex,__add,__call,__tostring的解释与应用实例,展示了如何通过元表实现表的扩展与重载。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

--元表,元表附属于表,用于表的扩展,是更底层的东西。
--元表的作用多种多样,其中一个就是可以在对普通表操作时方法的重载,用于扩展,因此一般情况下定义好后不需要再次修改元表。
----比如让两个表进行相加,如果没有元表的话直接相加编译器会报错的。
----还有就是用print输出表时,是直接输出这个表的内存块名,用了元表就可以很方便的输出表的元素

tab1 = {1,2,3,4,5,6}
met1 = {}
tab2 = setmetatable(tab1,met1)
met2 = getmetatable(tab1)

print(tab1,tab2) --输出:table: 00A395A0	table: 00A395A0
print(met1,met2) --输出:table: 00A39A50	table: 00A39A50

----------------------------------------------------

-- __metatable关键字,可以保护元表,禁止用户访问元表中的成员或者修改元表。

tab = setmetatable({"Lua","Java","C#","C++"} , {__metatable="lock"} )
print(getmetatable(tab)) --输出:lock

---------------------------------------------------------

--__index关键字,用于访问普通表不存在的Key时,所调用元表的扩展方法

tab1 = {1,2,3,4,5,6}
met1 = {
	__index = function(tab,key)
		print("tab",tab)
		print("调用了元表的__index方法")
		table.insert(tab1,key) --table表插入方法,参数1:table,参数2:[index]位置,参数3:value
	end}
tab2 = setmetatable(tab1,met1)
met2 = getmetatable(tab1)

function OnContainTab(tab,value)
	for k,v in pairs(tab) do
		if v == value then
			print("搜索到目标 k:"..k.." v:"..v)
			return true
		end
	end
	return false
end

print("tab1",tab1) --输出:tab1	table: 00B196D0
print("表的个数为: "..table.getn(tab1)) --输出:表的个数为: 6
print(tab1[100]) --输出:tab	table: 00B196D0 | 调用了元表的__index方法 | nil
print("表的个数为: "..table.getn(tab1)) --输出:表的个数为: 7
OnContainTab(tab1,100) --输出: 搜索到目标 k:7 v:100

---------------------------------------------------------------

-- __newindex关键字,在table添加新的数据时,会调用这个

tab1 = {1,2,3,4,5,6}
local met1 = {
		__newindex = function(tab,key,value) --元表关键字支持函数
		print("普通表中未找到Key:"..key)
		rawset(tab,key,value) --添加新的键值对
		--table.insert(tab,key,value)  --添加新的键值对
	end
	}

function OnContainTab(tab,value)
	for k,v in pairs(tab) do

		if v == value then
			print("搜索到目标 k:"..k.." v:"..v)
			return true
		end
		if k == value then
			print("搜索到目标 k:"..k.." v:"..v)
			return true
		end
	end

	return false
end

newtab1 = setmetatable(tab1,met1)

newtab1[7] = "qi" --输出:普通表中未找到Key:7
OnContainTab(newtab1,7) --输出:搜索到目标 k:7 v:qi


tab2 = {1,2,3,4,5,6}
tab3 = {}
local met2 = {
		__newindex = tab3 --元表关键字还支持Table
	}

newtab2 = setmetatable(tab2,met2)

newtab2[7] = "qi" --由于元表__newindex指向了tab3,因此当普通表tab2的索引查不到Key时,会自动地赋值给tab3
print(tab2[7]) --输出:nil
print(tab3[7]) --输出:qi 

---------------------------------------------------

-- add关键字,在table进行数学操作符时,进行扩展
tab1 = {1,3,5}
tab2 = {2,4,6}

local met1 = {
		__add = function(tab_1,tab_2) --add关键字,参数1:tab1,参数2:tab2
		for k,v in pairs(tab_2)do
			table.insert(tab_1,v)
		end
		return tab_1
	end
	}

newtab1 = setmetatable(tab1,met1)

tab3 = tab1+tab2

for k,v in pairs(tab3) do
	print(k,v) --输出:1	1 | 2	3 | 3	5 | 4	2 | 5	4
end

----------------------------------------------------

-- __call关键字,可把table当做函数来使用
tab1 = {1,3,5}

local met1 = {
		__call =
		function(tab,...) 使用"...",用于处理所有参数
			print(...)
			return tab
		end
	}

newtab1 = setmetatable(tab1,met1)
newtab1(123,123123) --输出:123	123123

------------------------------------------------------

-- __tosting关键字,用于直接打印table时,所扩展的方法

tab1 = {"一","二","三"}
local met1 = {
		__tostring =
		function(tab)
			local string = ""
			for k,v in pairs(tab) do
				string = string..k.." "..v.."|"
			end
			return string
		end
	}

newtab1 = setmetatable(tab1,met1)

print(newtab1) --元表没有__tostring关键字则输出:table: 00DB9438,加了则输出:1 一|2 二|3 三|

 

### Lua 中 `__index` 方法的用法 在 Lua 中,metatable)是一种强大的机制,用于定义对象的行为。当尝试访问一个键而该键不存在时,`__index` 方法会被触发[^1]。它可以通过两种方式设置: #### 1. 使用函数作为 `__index` 如果将一个函数赋值给 `__index` 字段,则每次访问未找到的键时都会调用这个函数。此函数接受两个参数:第一个是原始本身,第二个是要查找的键。 ```lua local mt = {} mt.__index = function(table, key) return "Key not found: " .. tostring(key) end local myTable = setmetatable({}, mt) print(myTable.someNonExistentKey) -- 输出: Key not found: someNonExistentKey ``` #### 2. 使用另一个作为 `__index` 更常见的做法是将另一个赋值给 `__index` 字段。这样,在原中找不到某个键时,Lua 将自动在这个替代中继续寻找对应的值。 ```lua local defaultValues = {a=1, b=2} local mt = {__index = defaultValues} local myTable = setmetatable({b=3}, mt) -- 访问存在的键 print(myTable.a) -- 输出: 1 (来自defaultValues) print(myTable.b) -- 输出: 3 (覆盖了默认值) -- 访问不存在的键 print(myTable.c) -- 输出: nil (既不在myTable也不在defaultValues中存在) ``` 这种模式非常有用,尤其是在实现继承关系或者提供默认配置的情况下[^2]。 #### 结合 C API 的例子 通过 Lua 的 C API 可以更加灵活地操作带有 `__index` 方法的对象。例如,可以利用 `lua_gettable` 函数来模拟 `__index` 行为: ```c #include <lauxlib.h> #include <lua.h> int l_my_index(lua_State* L){ // 获取 table 和 key 参数 lua_pushvalue(L, 2); /* duplicate the key */ lua_rawget(L, lua_upvalueindex(1)); /* lookup in 'parent' */ if (lua_isnil(L, -1)) { lua_pop(L, 1); lua_pushstring(L, "Not Found"); } return 1; } void setup_metatable(lua_State *L){ luaL_newmetatable(L, "MyType"); lua_pushcfunction(L, l_my_index); lua_setfield(L, -2, "__index"); lua_pop(L, 1); } ``` 在此代码片段中,我们创建了一个自定义类型的,并为其设置了 `__index` 方法。每当尝试读取未知字段时,C 函数 `l_my_index` 被执行并试图从父级结构检索数据[^3]。 ### 总结 无论是纯 Lua 还是在扩展库中的应用,理解如何正确运用 `__index` 都能极大地增强程序的功能性和灵活性。它可以用来简化复杂的数据查询逻辑或是构建面向对象编程模型的基础组件之一。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值