首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> 關於C語言

Lua 元表與元方法深度解析:從入門到實戰

     編輯:關於C語言

在 C++ 中,我們可以透過運算子多載讓兩個類別相加;同樣地,Lua 雖然不支援直接對兩個 table 進行「+」運算,但我們可以透過元表與元方法來實現這一機制。本文將系統整理 Lua 中元表的核心概念、常見元方法的用法,並搭配完整程式碼示範,幫助你掌握這項進階技巧。

什麼是元表?

Lua 中每個值都擁有一組預定義操作(如數字可相加、字串可連接),但 table 預設不支援算術運算。元表就是一個普通的 table,透過 setmetatable 關聯到目標 table,當發生非預定義操作(如 a + b)時,Lua 會依序檢查:

  1. 操作數是否有元表?
  2. 元表中是否存在對應的元方法(如 __add)?
  3. 若存在,呼叫該元方法並傳入操作數;
  4. 若不存在或查找失敗,拋出錯誤。

table 和 userdata 可各自擁有獨立元表,其他型別則共享型別層級的單一元表。字串在標準庫載入後會自動擁有元表,數字等基本型別則預設無元表。

基本操作:建立與存取元表

local t = {1, 2}
print(getmetatable(t))  -- nil

local mt = {}
setmetatable(t, mt)
assert(getmetatable(t) == mt)

任何 table 皆可作為元表,甚至可自我作為元表。僅能在 Lua 程式碼中設定 table 的元表,其他型別需透過 C API 設定。

常見元方法一覽

元方法對應操作
__add加法 (+)
__sub減法 (-)
__mul乘法 (*)
__div除法 (/)
__mod取模 (%)
__pow乘冪 (^)
__unm相反數 (-a)
__concat連接 (..)
__len長度 (#)
__eq相等 (==)
__lt小於 (<)
__le小於等於 (<=)
__index索引查詢
__newindex索引更新
__call函式呼叫
__tostring字串轉換
__metatable保護元表

實戰範例:集合運算

以下示範如何用 __add 實現集合並集:

Set = {}
local mt = {}

function Set.new(l)
    local set = {}
    setmetatable(set, mt)
    for _, v in pairs(l) do set[v] = true end
    return set
end

function Set.union(a, b)
    local res = Set.new{}
    for v in pairs(a) do res[v] = true end
    for v in pairs(b) do res[v] = true end
    return res
end

mt.__add = Set.union

local s1 = Set.new{10, 20, 30}
local s2 = Set.new{1, 2}
local s3 = s1 + s2  -- 自動呼叫 Set.union

雙元運算子的元表查找規則

  1. 第一個操作數有元表且含所需元方法 → 使用該元方法;
  2. 否則檢查第二個操作數;
  3. 皆無則報錯。

為避免型別不符導致難以除錯,建議在元方法中檢查元表一致性:

function Set.union(a, b)
    if getmetatable(a) ~= mt or getmetatable(b) ~= mt then
        error("metatable error: 僅支援 Set 之間的運算")
    end
    -- ...
end

關鍵元方法詳解

__tostring:自訂輸出格式

mt.__tostring = Set.toString
print(s1)  -- {10, 20, 30}

__metatable:保護元表不被竄改

mt.__metatable = "You cannot access the metatable"
print(getmetatable(s1))  -- You cannot access the metatable
setmetatable(s1, {})     -- error: cannot change a protected metatable

__index:預設值與繼承

當索引不存在時觸發,可為函式或 table:

Window = {}
Window.default = {x=0, y=0, w=100, h=100}
Window.mt = {}

function Window.new(o)
    setmetatable(o, Window.mt)
    return o
end

Window.mt.__index = Window.default  -- 直接指向預設值表

local w = Window.new{x=10, y=20}
print(w.w)  -- 100 (來自 default)

__newindex:攔截新增欄位

對不存在的索引賦值時觸發,同樣支援函式或 table 導向。注意避免循環參考導致 loop in settable 錯誤。

rawget / rawset:繞過元方法

rawget(t, k)   -- 直接讀取,不觸發 __index
rawset(t, k, v) -- 直接寫入,不觸發 __newindex

總結

元表與元方法是 Lua 物件導向與運算子多載的基石。掌握 __index/__newindex 可實現繼承與預設值,__add 等算術元方法賦予 table 數學語意,__tostring/__metatable 則提升除錯體驗與封裝性。深入理解這些機制,是玩轉 Lua 的關鍵。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved