Lua 面向对象(详解)

Lua 面向对象(详解)

参考文章:

https://blog.csdn.net/linxinfa/article/details/103254828

https://zhuanlan.zhihu.com/p/115159195

https://blog.codingnow.com/cloud/LuaOO

https://blog.codingnow.com/2006/06/oo_lua.html

  Lua的面向对象主要是参考云风大神的进行编写,在云风大神源码的基础上考虑了一点细节,构造代码如下:

local _class = {}

function class(super)
    local tbClassType = {}
    tbClassType.Ctor  = false
    tbClassType.super = super
    tbClassType.New   = function(...)
        local tbObj   = {}
        do
            local funcCreate
            funcCreate = function(tbClass,...)
                if tbClass.super then
                    funcCreate(tbClass.super,...)
                end
                
                if tbClass.Ctor then
                    tbClass.Ctor(tbObj,...)
                end
            end
            funcCreate(tbClassType,...)
        end
        -- 防止调用Ctor初始化时,在Ctor内部设置了元表的情况发生
        if getmetatable(tbObj) then
            getmetatable(tbObj).__index = _class[tbClassType] 
        else
            setmetatable(tbObj, { __index = _class[tbClassType] })
        end
        return tbObj
    end

    local vtbl          = {} 
    _class[tbClassType] = vtbl

    setmetatable(tbClassType, { __newindex = 
        function(tb,k,v)
            vtbl[k] = v
        end
    })

    if super then
        setmetatable(vtbl, { __index = 
            function(tb,k)
                local varRet = _class[super][k]
                vtbl[k]      = varRet
                return varRet
            end
        })
    end
    return tbClassType
end


baseType = class()

function baseType:Ctor(x)
    print("baseType Ctor")
    self.x = x
    -- do something
    -- 包括setmetatable(self,{xxx})
    -- 由于在New方法内部已经考虑如果在Ctor函数中设置元表,外部则直接对__index赋值。毕竟一个表只能关联一张元表
end

function baseType:PrintX()
    print(self.x)
end


function baseType:Hello()
    print('hello baseType')
end


test = class(baseType)

function test:Ctor()	-- 定义 test 的构造函数
	print("test Ctor")
end
 
function test:Hello()	-- 重载 base_type:hello 为 test:hello
	print("hello test")
end


a = test.New(1)
a:PrintX()
a:Hello()

a1 = test.New(2)	
a1:PrintX()
a1:Hello()
a:PrintX()	

输出结果:

效果图

  下面对上述代码进行剖析以及注释。上述代码不仅仅实现类的封装以及继承,对元表中的元方法进行重载也已经考虑。

-- 记录所有的类模板以及其对应的方法,同时可以利用在继承关系下快速索引父级属性以及方法。
local _class = {}

-- 构建一个类,返回一个类对象,参数为基类(可不填)
function class(super)
    -- 可以理解为构建一个类模板
    local tbClassType = {}
    
    -- 构造函数,可以理解为C++的纯虚函数(接口)
    tbClassType.Ctor  = false
    
    -- 子类记录父类
    tbClassType.super = super

    -- New成员方法(匿名方法,闭包),只要用于通过类模板实例化一个类对象(实例)
    tbClassType.New   = function(...)
        -- 实例类
        local tbObj   = {}
        do
            -- 用于递归调用,如果有基类,且基类有构造函数,那么先调用基类的构造函数;否则直接调用当前类的构造函数
            local funcCreate
            funcCreate = function(tbClass,...)
                if tbClass.super then
                    -- 如果有基类,优先调用基类的ctor函数(递归,直至没有上一级为止)
                    funcCreate(tbClass.super,...)
                end
                
                if tbClass.Ctor then
                    -- 调用构造函数
                    tbClass.Ctor(tbObj,...)
                end
            end
            funcCreate(tbClassType,...)
        end
        -- 防止调用Ctor初始化时,在Ctor内部设置了元表的情况发生
        -- _class[tbClassType]就是类模板绑定的成员,类实例同样需要持有
        if getmetatable(tbObj) then
            getmetatable(tbObj).__index = _class[tbClassType] 
        else
            setmetatable(tbObj, { __index = _class[tbClassType] })
        end
        return tbObj
    end

    -- 这个table保存所有类模板的成员,实现过程中主要保存的是类的成员方法
    local vtbl          = {} 
    _class[tbClassType] = vtbl

    -- tbClassType 新增成员的时候,存到vtbl中
    setmetatable(tbClassType, { __newindex = 
        function(tb,k,v)
            vtbl[k] = v
        end
    })

    if super then
        -- 如果当前类继承了父类,那么在访问成员时,如果当前类中不具有该成员,则优先从_class中保存的父级中读取;
        -- 然后再将父类的成员赋值给当前类模板的vtbl容器。
        -- 简单地说,就似乎如果一个类实例有需要访问的成员则直接调用,否则从该类的上一级查找。
        setmetatable(vtbl, { __index = 
            function(tb,k)
                local varRet = _class[super][k]
                vtbl[k]      = varRet
                return varRet
            end
        })
    end
    return tbClassType
end


baseType = class()

function baseType:Ctor(x)
    print("baseType Ctor")
    self.x = x
end


function baseType:PrintX()
    print(self.x)
end


function baseType:Hello()
    print('hello baseType')
end


test = class(baseType)

function test:Ctor()	-- 定义 test 的构造函数
	print("test Ctor")
end
 
function test:Hello()	-- 重载 base_type:hello 为 test:hello
	print("hello test")
end


a = test.New(1)
a:PrintX()
a:Hello()

a1 = test.New(2)	
a1:PrintX()
a1:Hello()
a:PrintX()	

说明点

  上面有些细节的地方需要阅读一下知识点,有助于理解上文代码。

__index和__newindex

  当我们一个表绑定一个元表时,如果为访问表的属性,那么触发的是__index,而如果是修改表的属性,那么触发的是__newindex。也就是说,我们在修改表的属性时并不会遵循__index的查找规则!

tbTest = {}
setmetatable(tbTest,{ __index = function(mytable, key)
    if key == "key2" then
      return "metatablevalue"
    else
      print(key)
    end
  end})

tbTest.name = "ufgnix0802" -- 这里并不会触发__index这个元方法
print(tbTest.name)         -- 触发__index元方法!

getmetatable(tbTest).__newindex =  function(mytable, key, value)
        rawset(mytable, key, "\""..value.."\"")
    end

tbTest.key = "ufgnix0802" -- 触发__newindex元方法!

‘self’和’:'的关系

  在lua中,表拥有一个标识:self。self类似于this指针,大多数面向对象语言都隐藏了这个机制,在编码时不需要显示的声明这个参数,就可以在方法内使用this(例如C++和C#)。在lua中,提供了冒号操作符来隐藏这个参数,例如:

local t = {a = 1, b = 2}
function t:Add()
    return (self.a + self.b)
end

print(t:Add())

  冒号的作用有两个:1. 对于方法定义来说,会增加一个额外的隐藏形参(self);2. 对于方法调用来说,会增加一个额外的实参(表自身)。

  冒号只是一种语法机制,提供的是便利性,并没有引入任何新的东西。使用冒号完成的事情,都可以使用点语法来完成。看下面的例子:

local t = {a = 1, b = 2}
function t:Add()
    return (self.a + self.b)
end
function t.Sub(self)
    return (self.a - self.b)
end

print(t.Add(t))
print(t:Sub())

  从上述我们可以很清楚的知道,‘self’和‘:’配对出现,而我们也可不使用这种方式,但是必须传递类本身。那么关键问题来了,如果方法要调用的方法使用了’:‘符号,但我们又不想使用’:'调用呢?那么是不是我们得传递一个类对象本身?这也是上文代码New函数中funcCreate的调用方式为什么多了一个参数,也就是第一个参数tbClass的原因了。

  同时,我们需要留意一个细节,那就是上文代码第30行,我们传入的是tbObj,即我们即将构建的类对象实例的指针,而不是第90行的test对象本身。这也是为什么在第92行打印self 的地址跟test不相等的原因。感兴趣的可以尝试一下,self的地址跟101行的a以及105行的a1相同。

实际使用实例

文章链接索引:

https://blog.csdn.net/qq135595696/article/details/128827673

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ufgnix0802

总结不易,谢谢大家的支持

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值