概述
lua的__index元方法本身沒有提供類似C++虛函式機制,呼叫的父類方法呼叫虛函式可能會出現問題,
問題分析
分析這段代碼和輸出
local Gun = {}
-- 示例,實際應用還要考慮構造,虛表等情況
function LuaClass(Class, Parent)
setmetatable(Class, {__index = Parent})
Class._Super = Parent
end
function Gun:Attack()
print("開始攻擊");
self:Load()
self:Fire()
end
function Gun:Load()
print("裝彈");
end
function Gun:Fire()
print("開槍");
end
Gun:Attack();
local Cannon = {}
LuaClass(Cannon, Gun)
function Cannon:Attack()
print("大炮開始攻擊")
self._Super:Attack()
end
function Cannon:Fire()
print("開炮")
end
print("-------------------------------------")
Cannon:Attack()
輸出:

紅線圈出的地方虛函式呼叫錯誤,應該列印"開炮",
使用元表來面向物件時,要注意__index元方法的語意:
當你通過鍵來訪問 table 的時候,如果這個鍵沒有值,那么Lua就會尋找該table的metatable(假定有metatable)中的__index 鍵,如果__index包含一個表格,Lua會在表格中查找相應的鍵
如果__index包含一個函式的話,Lua就會呼叫那個函式,table和鍵會作為引數傳遞給函式,
__index 元方法查看表中元素是否存在,如果不存在,回傳結果為 nil;如果存在則由 __index 回傳結果
可知__index只是提供一種遞回的查詢方式,其中并未包含虛函式的呼叫機制,
而
Gun:Attack() 等價于 Gun.Attack(self)
self._Super:Attack() 等價于 Gun.(Gun) 注意self._Super = Gun
所以呼叫父類Attack函式中,self的語意是Gun這張表,后面呼叫的就一直是Gun方法,所以最后呼叫的是Gun的Fire,而不是Cannon的Fire,
解決方案
使用指標指向呼叫函式的表,在呼叫父類的方法時,使父類的self的語意是呼叫者,
注意這種實作和C++的虛函式呼叫思路是不一樣的,細節請參考我的另一篇文章:
跳轉鏈接:c++虛函式表、多型
替換問題分析中的LuaClass方法
function LuaClass(Class, Parent)
local FindVal = function(InClass, Key)
local Raw = rawget(InClass, Key)
if nil ~= Raw then
return Raw, InClass
end
if nil ~= InClass.__Base then
return FindVal(InClass.__Base, Key)
end
end
Class.__Base = Parent
Class.__ClassPtr = Class
local Index = function(_, Key)
local Val, ClassPtr = FindVal(Parent, Key)
if nil == Val then
return
end
Class.__ClassPtr = ClassPtr
return Val
end
setmetatable(Class, {__index = Index})
local SuperIndex = function(_, Key)
return function(_, ...)
local OriClassPtr = Class.__ClassPtr
if nil == OriClassPtr.__Base then
return
end
local Val, ClassPtr = FindVal(OriClassPtr.__Base, Key)
if nil == Val then
return
end
Class.__ClassPtr = ClassPtr
local Ret = {Val(Class, ...)}
Class.__ClassPtr = OriClassPtr
return table.unpack(Ret)
end
end
Class._Super = setmetatable({}, {__index = SuperIndex})
end
輸出:

- 在__index元方法查詢的時候,標記當前呼叫方法所在的表,
- 在_Super的元表__index元方法查詢的時候,找到標記表的方法,使用Class表作為第一個引數self傳入,
備注
- 支持虛函式有性能開銷,可以在LuaClass加個引數控制是否支持虛函式,
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/507180.html
標籤:其他
下一篇:動態格子演算法
