Lua 中 metatable 是一個(gè)普通的 table,但其主要有以下幾個(gè)功能:
1.定義算術(shù)操作符和關(guān)系操作符的行為
2.為 Lua 函數(shù)庫提供支持
3.控制對 table 的訪問
Metatables 定義操作符行為
Metatable 能夠被用于定義算術(shù)操作符和關(guān)系操作符的行為。例如:Lua 嘗試對兩個(gè) table 進(jìn)行加操作時(shí),它會(huì)按順序檢查這兩個(gè) table 中是否有一個(gè)存在 metatable 并且這個(gè) metatable 是否存在 __add 域,如果 Lua 檢查到了這個(gè) __add 域,那么會(huì)調(diào)用它,這個(gè)域被叫做 metamethod。
Lua 中每個(gè) value 都可以有一個(gè) metatable(在 Lua 5.0 只有 table 和 userdata 能夠存在 metatable)。每個(gè) table 和 userdata value 都有一個(gè)屬于自己的 metatable,而其他每種類型的所有 value 共享一個(gè)屬于本類型的 metatable。在 Lua 代碼中,通過調(diào)用 setmetatable 來設(shè)置且只能設(shè)置 table 的 metatable,在 C/C++ 中調(diào)用 Lua C API 則可以設(shè)置所有 value 的 metatable。默認(rèn)的情況下,string 類型有自己的 metatable,而其他類型則沒有:
Metamethod 的參數(shù)為操作數(shù)(operands),例如:
每個(gè)算術(shù)操作符有對應(yīng)的 metamethod:
+ | __add |
* | __mul |
- | __sub |
/ | __div |
- | __unm (for negation) |
% | __mod |
^ | __pow |
對于連接操作符有對應(yīng)的 metamethod:__concat
同樣,對于關(guān)系操作符也都有對應(yīng)的 metamethod:
== | __eq |
__lt | |
= | __le |
其他的關(guān)系操作符都是用上面三種表示:
a ~= b 表示為 not (a == b)
a > b 表示為 b a
a >= b 表示為 b = a
和算術(shù)運(yùn)算符不同的是,關(guān)系運(yùn)算符用于比較擁有不同的 metamethod(而非 metatable)的兩個(gè) value 時(shí)會(huì)產(chǎn)生錯(cuò)誤,例外是比較運(yùn)算符,擁有不同的 metamethod 的兩個(gè) value 比較的結(jié)果是 false。
不過要注意的是,在整數(shù)類型的比較中 a = b 可以被轉(zhuǎn)換為 not (b a),但是如果某類型的所有元素并未適當(dāng)排序,此條件則不一定成立。例如:浮點(diǎn)數(shù)中 NaN(Not a Number)表示一個(gè)未定義的值,NaN = x 總是為 false 并且 x NaN 也總為 false。
為 Lua 函數(shù)庫提供支持
Lua 庫可以定義和使用的 metamethod 來完成一些特定的操作,一個(gè)典型的例子是 Lua Base 庫中 tostring 函數(shù)(print 函數(shù)會(huì)調(diào)用此函數(shù)進(jìn)行輸出)會(huì)檢查并調(diào)用 __tostring metamethod:
另外一個(gè)例子是 setmetatable 和 getmetatable 函數(shù),它們定義和使用了 __metatable 域。如果你希望設(shè)定的 value 的 metatable 不被修改,那么可以在 value 的 metatable 中設(shè)置 __metatable 域,getmetatable 將返回此域,而 setmetatable 則會(huì)產(chǎn)生一個(gè)錯(cuò)誤:
看一個(gè)完整的例子:
控制 table 的訪問
__index metamethod
在我們訪問 table 的不存在的域時(shí),Lua 會(huì)嘗試調(diào)用 __index metamethod。__index metamethod 接受兩個(gè)參數(shù) table 和 key:
__index 域也可以是一個(gè) table,那么 Lua 會(huì)嘗試在 __index table 中訪問對應(yīng)的域:
我們通過 __index 可以容易的實(shí)現(xiàn)單繼承(類似于 JavaScrpit 通過 prototype 實(shí)現(xiàn)單繼承),如果 __index 是一個(gè)函數(shù),則可以實(shí)現(xiàn)更加復(fù)雜的功能:多重繼承、caching 等。我們可以通過 rawget(t, i) 來訪問 table t 的域 i,而不會(huì)訪問 __index metamethod,注意的是,不要太指望通過 rawget 來提高對 table 的訪問速度(Lua 中函數(shù)的調(diào)用開銷遠(yuǎn)遠(yuǎn)大于對表的訪問的開銷)。
__newindex metamethod
如果對 table 的一個(gè)不存在的域賦值時(shí),Lua 將檢查 __newindex metamethod:
1.如果 __newindex 為函數(shù),Lua 將調(diào)用函數(shù)而不是進(jìn)行賦值
2.如果 __newindex 為一個(gè) table,Lua 將對此 table 進(jìn)行賦值
如果 __newindex 為一個(gè)函數(shù),它可以接受三個(gè)參數(shù) table key value。如果希望忽略 __newindex 方法對 table 的域進(jìn)行賦值,可以調(diào)用 rawset(t, k, v)
結(jié)合 __index 和 __newindex 可以實(shí)現(xiàn)很多功能,例如:
1.OOP
2.Read-only table
3.Tables with default values
Read-only table
有時(shí)候,我們需要為 table 設(shè)定一個(gè)唯一的 key,那么可以使用這樣的技巧:
標(biāo)簽:延邊 張掖 黑龍江 宜賓 江西 嘉峪關(guān) 武漢 新余
巨人網(wǎng)絡(luò)通訊聲明:本文標(biāo)題《Lua中的metatable詳解》,本文關(guān)鍵詞 Lua,中的,metatable,詳解,Lua,;如發(fā)現(xiàn)本文內(nèi)容存在版權(quán)問題,煩請?zhí)峁┫嚓P(guān)信息告之我們,我們將及時(shí)溝通與處理。本站內(nèi)容系統(tǒng)采集于網(wǎng)絡(luò),涉及言論、版權(quán)與本站無關(guān)。