虚函数,解决Lua 支持虚函数

科技资讯 投稿 28300 0 评论

虚函数,解决Lua 支持虚函数

概述

问题分析

分析这段代码和输出

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元方法的语义:

如果__index包含一个函数的话,Lua就会调用那个函数,table和键会作为参数传递给函数。
__index 元方法查看表中元素是否存在,如果不存在,返回结果为 nil;如果存在则由 __index 返回结果


Gun:Attack( 等价于 Gun.Attack(self
self._Super:Attack( 等价于 Gun.(Gun 注意self._Super = Gun
所以调用父类Attack函数中,self的语义是Gun这张表,后面调用的就一直是Gun方法,所以***调用的是Gun的Fire,而不是Cannon的Fire。

解决方案

注意这种实现和C++的虚函数调用思路是不一样的

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加个参数控制是否支持虚函数。

编程笔记 » 虚函数,解决Lua 支持虚函数

赞同 (36) or 分享 (0)
游客 发表我的评论   换个身份
取消评论

表情
(0)个小伙伴在吐槽