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

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

运行结果

相关推荐
程序猿乐锅2 分钟前
【黑马点评 | 第二篇】Redis 缓存更新策略与商铺缓存实现
java·网络·redis·spring·mybatis
livemetee4 分钟前
MySQL联合唯一索引(code,deleted)与逻辑删除问题
java·数据库
MacroZheng7 分钟前
Redis官方发布高颜值可视化工具,功能更是强的离谱!
java·redis·后端
wuminyu10 分钟前
纯轻量级锁体系下C2编译器锁粗化和消除机制剖析
java·linux·c语言·jvm·c++
ss27321 分钟前
AI全栈实战 | 1.4-02 Spring AOP:@Transactional 失效的 N 种场景,根因其实只有一个
java·spring·ai编程
weilx123425 分钟前
C++笔记-mutex
c++
beijixinghe35 分钟前
第15节 指针作为函数参数的工程实战用法
开发语言·c++·c++基础·c++入门·几何引擎c++
梦想平凡43 分钟前
情怀棋牌源代码焕新记录(一):全新国风UI与原版工程梳理
java·服务器·数据库·websocket·网络协议·cocos2d
j7~1 小时前
【C++微服务项目开发脚手架】(准备篇)从 0 到跑通:Docker 容器开发环境 + 宿主机用户 + Trae 远程连接 全链路踩坑实战
linux·c++·学习·xshell·docker容器·trae
和裕1 小时前
平口开槽箱 vs 飞机盒 vs 扣底盒:自动化、展示效果与成本核心区别
大数据·运维·网络·人工智能·算法·自动化