Bootstrap

Lua面向对象 实现 超详细注释 实现构造函数,析构函数,只读类模板等功能

Lua面向对象 实现 超详细注释 实现构造函数,析构函数,只读类模板等功能

源码

-- 注意下面的代码可以拆开成多个文件使用,也可以放一起
-- Class.lua

local _class = {}

-- 将Source变成只读表并返回
function MakeTableReadOnly(Source)
    local proxy = {}
    local mt = {}
    mt.__index = Source
    mt.__newindex = function()
        print("ReadOnly!")
    end
    setmetatable(proxy,mt)
    return proxy
end


-- 返回class_type作为类模板,类模板可用来创建对象
function class(super)
    local class_type = {}
    -- ctor为构造函数
    class_type.ctor = false
    -- close为析构函数
    class_type.close = false
    -- super为父类,也是一个class_type
    class_type.super = super
    -- 类模板提供一个创建类实例的方法
    class_type.new = function(...)
        -- object为返回的类实例
        local object = {}
        -- create用于从父类一直递归的调用构造函数,c是class_type
        local create
        create = function(c,...)
            if c.super then
                create(c.super, ...)
            end
            if c.ctor then
                c.ctor(object, ...)
            end
        end
        -- 调用构造函数
        create(class_type,...)

        -- 构建元表
        local mt = {}
        -- 如果访问object中没有的对象,那么就访问_class[class_type],这样就实现了成员的继承
        mt.__index = _class[class_type]

        -- 析构函数
        local close_function = function(...)
            local destory
            destory = function(c,...)
                if c.close then
                    c.close(object,...)
                end
                if c.super then
                    destory(c.super,...)
                end
            end
            destory(class_type, ...)
        end
        -- 当类实例被gc掉时,触发close_function
        mt.__gc = close_function

        -- 设置元表
        setmetatable(object, mt)
        return object
    end

    -- vtbl用于存放类模板的公用不可变成员,也就是类的成员(这里存放的是类中不可修改的,公用的部分,通常是函数或者常数)
    local vtbl = {}

    -- 类模板的成员不可修改
    _class[class_type] = MakeTableReadOnly(vtbl)

    -- 对类模板的新建值操作,实际上会存到vtbl中,可以实现函数的覆盖
    setmetatable(class_type, {__newindex = function(t,k,v)
        rawset(vtbl,k,v)
    end})

    -- 如果有父类
    if super then
        -- 如果访问类模板成员表中没有,就访问父类模板成员表
        setmetatable(vtbl, {__index = function(t,k)
            local res = _class[super][k]
            -- 下面这行可以加可以不加,加上的话,这里会将父类的成员直接拷贝到子类中,之后再次访问的话效率会提高。但是热更新的情况下如果父类模板修改,子类不会修改。
            -- rawset(vtbl,k,res)
            return res
        end})
    end

    -- 返回类模板
    return class_type
end

-- Base.lua
-- 创建类模板
Base = class()

-- 这里不会触发class_type的__newindex元方法,因为class_type中已经有了
function Base:ctor(x)
    print("Base:ctor")
    -- 这里的self是类实例,而不是类模板,因为这个函数由上面的create调用,create传递的参数是object(类实例),根据:操作符,self就是object
    self.x = x
end

-- 同理不会触发__newindex元方法
function Base:close(x)
    print("Base:close")
end

-- 这里会触发class_type的__newindex元方法,将Hello方法加入到_class[class_type](也就是vtbl)中
function Base:Hello()
    print("Base:Hello")
end

-- SubBase.lua
-- 创建SubBase 继承于 Base
SubBase = class(Base)

-- 同理不会触发__newindex元方法
function SubBase:ctor(x,y)
    print("SubBase:ctor")
    -- 这里的self是SubBase的类实例对象,由于create是递归的调用ctor方法,所以self中既有x也有y。
    -- 为何要这样,通过ctor中使用self的这种方式,每个类实例对象的成员都是不同的,修改成员时不会相互影响
    self.y = y
end

-- 同理不会触发__newindex元方法
function SubBase:close(x)
    print("SubBase:close")
end

-- 这里实现了Hello函数的override,原理是vtbl绑定了__index元方法,只有__index元方法只有找不到的时候,才会返回父类成员
-- 由于class_type实现了__newindex方法,这里定义的函数会向vtbl中写入,从而实现函数的override
function SubBase:Hello()
    print("SubBase:Hello")
end

-- 向vtbl中写入
function SubBase:SubHello()
    print(self.x)
    print(self.y)
end

-- Test.lua
-- 注意这里要使用.而不是:
-- 创建类实例
Test = Base.new(1)
Test2 = SubBase.new(1,2)

Test:Hello()
Test2:Hello()
Test2:SubHello()

-- 尝试修改类模板的成员
getmetatable(Test2)["__index"].Hello = function()
    print("want to change!")
end

运行结果

在这里插入图片描述

;