Lua 面向对象(详解)

2023-05-16

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

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Lua 面向对象(详解) 的相关文章

  • 为什么这个 Lua 优化 hack 会提高性能?

    我正在寻找一个描述提高 Lua 性能的各种技术的文档 http www lua org gems sample pdf脚本代码 我很震惊竟然需要这样的技巧 虽然我引用的是 Lua 但我在 Javascript 中也见过类似的 hack 为什
  • 如何使用循环将字符串连接成一个?

    有人可以帮我解决字符串连接问题吗 我从寄存器读取数据 它的函数 utf regAddr length 我得到带有十进制数字的表格 然后将其转换为十六进制并循环字符串 我需要将这些字符串连接成一个 Lua中没有像 这样的操作符 functio
  • 创建独立的 Lua 可执行文件

    有没有一种简单的方法可以从 Lua 脚本创建独立的 exe 文件 基本上这将涉及链接 Lua 解释器和脚本 我相信这是可能的 PLT 方案允许以相同的方式创建独立的可执行文件 但是到底是如何实现的呢 查看srlua http www tec
  • Lua:冒号符号、“自我”和函数定义与调用

    我对定义 调用 Lua 函数时使用的冒号表示法感到非常困惑 我以为我已经明白了 直到我看到这段代码 function string PatternSafe str return str gsub pattern escape replace
  • 循环直到在表中找到 2 个特定值?

    我试图找到一种更聪明的方法来解决这个问题 这是与游戏相关的代码的摘录 它循环遍历每个背包的每个插槽 直到找到铲子和绳子 local continue local foundShovel foundRope for i 0 Container
  • 在 Corona sdk 上保存高分?

    我想保存游戏中创建的高分 并且当玩家点击高分按钮时可以在主菜单中看到 有人可以帮助我吗 您可以使用SQLITE https docs coronalabs com api library sqlite3 index html将高分保存到数据
  • lua http套接字超时

    LuaSocket HTTP 模块文档说可以在 HTTP 连接上设置超时 可以设置以下常量来控制 HTTP 模块的默认行为 PORT 用于连接的默认端口 PROXY 用于连接的默认代理 TIMEOUT 设置所有I O操作的超时时间 USER
  • 如何在aerospike中获取ttl为-1的记录集?

    我在aerospike中有很多记录 我想获取ttl为 1的记录 请提供解决方案 只是为了澄清 设置TTL 为 1 https github com aerospike aerospike client go blob master docs
  • Lua userdata:无法同时进行数组访问和方法

    我遇到了这个人的问题 Lua userdata数组访问及方法 https stackoverflow com questions 26970316 lua userdata array access and methods 其中 当我设置用
  • 为什么 LuaJIT 这么好?

    编辑 不幸的是 LuaJIT 已从下面链接的比较中删除 This 比较 http shootout alioth debian org u64 which programming languages are fastest php编程语言的
  • Lua 从 5.1 更新 - LUA_GLOBALSINDEX 问题

    我最近将旧的 Lua 5 1 项目更新到了该库的最新版本 但遇到了问题LUA GLOBALSINDEX 它变得不确定 我只用过它lua getfield函数 像这样 void luastartgame void if startgamefu
  • Lua中按字符分割字符串

    我有像这样的字符串 ABC DEF 我需要将它们分开 字符并将两个部分分别分配给一个变量 在 Ruby 中 我会这样做 a b ABC DEF split 显然Lua没有这么简单的方法 经过一番挖掘后 我找不到一种简短的方法来实现我所追求的
  • Lua 将字符串转换为数字 - 取决于语言环境

    刚刚注意到字符串 and tonumber 在 Lua 中是依赖于语言环境的 知道如何在不使用的情况下将字符串转换为数字tonumber 谢谢 例如将字符串 58 5 转换为 58 5 另外 当我尝试将带点的数字传递给函数时 该函数会转换
  • 比较 Lua 中的日期

    我有一个带有日期表的变量 如下所示 table day number 15 year number 2015 month number 2 如何获取当前日期与上述日期之间的天数 非常感谢 您可以使用os time 将表转换为秒并获取当前时间
  • Lua C API:初始化结构体 C 中的变量矩阵

    我正在尝试使用 Lua C API 创建一个用户数据 并在其中关联一个元表 我将在其中收集矩阵 我无法得到的是如何将初始化矩阵的每个分量设置为零 我按照我的描述编译我的 Lua 模块 C 代码here https stackoverflow
  • 关闭 Löve2D 中的抗锯齿功能

    我在用着L ve2D http love2d org用于编写一个小游戏 L ve2D 是 Lua 的开源游戏引擎 我遇到的问题是 当您在非整数位置绘制精灵时 某些抗锯齿过滤器会自动应用于精灵 love graphics draw sprit
  • Lua 上的 For 循环

    我的作业是如何执行 for 循环 我已经从数字上弄清楚了 但无法从名称上弄清楚 我想创建一个 for 循环来运行名称列表 以下是我到目前为止所拥有的 names John Joe Steve for names 1 3 do print n
  • Lua 的标准(或最好支持的)大数(任意精度)库是什么?

    我正在处理大量无法四舍五入的数字 使用 Lua 的标准数学库 似乎没有方便的方法来保持精度超过某些内部限制 我还看到有几个库可以加载以处理大数字 http oss digirati com br luabignum http oss dig
  • 如何从 Lua 内部运行另一个脚本?

    我需要从另一个 Lua 脚本中执行一个 Lua 脚本 有多少种方法 我该如何使用它们 通常您会使用以下内容 dofile filename lua 但你可以通过以下方式做到这一点require 很好 例子 foo lua io write
  • 在Windows上使用gcc 5.3.0编译Lua 5.2.4模块

    我需要用 gcc 5 3 0 编译 Lua 5 2 4 模块 在 Windows 上 在此之前 我按照以下步骤构建 Lua 5 2 4 gcc c DLUA BUILD AS DLL c ren lua o lua obj ren luac

随机推荐

  • jetson系列硬件性能对比

    参考 Jetson Modules https developer nvidia com embedded jetson modules 第一种 4G 价格 1000元 1x 4K30 2x1080p60 4x1080p30 4x720p6
  • 遍历_EPROCESS->ObjectTable->HandleTableList链表枚举进程

    include lt ntifs h gt include lt ntddk h gt UCHAR PsGetProcessImageFileName in PEPROCESS Process HANDLE PsGetProcessInhe
  • Ubunut18.04安装Autoware1.12

    官方提供了两种安装Autoware的方法 xff1a Docker和Source两种方式 下边以Ubuntu18 04环境为例 xff0c 记录Source的安装方法 1 配置要求 1 1硬件 1 2软件 Qt安装 qmake span c
  • PuTTY在远程连接服务器之后,经常会断线提示“Software caused connection abort”,而且经常在很短的时间内就失去连接

    解决方案如下 xff1a 第一步 xff1a 设置服务器 1 修改服务器中 etc ssh sshd config 文件 xff0c 将LoginGraceTime的值设为0 xff0c TCPKeepAlive 设为yes 2 servi
  • Go语言循环语句

    Go语言循环语句 资料参考至菜鸟教程 在不少实际问题中有许多具有规律性的重复操作 xff0c 因此在程序中就需要重复执行某些语句 以下为大多编程语言循环程序的流程图 xff1a Go语言提供了以下几种类型循环处理语句 xff1a 循环类型描
  • Lua 协同程序(coroutine)

    Lua 协同程序 coroutine 参考文章 xff1a 菜鸟教程 https zhuanlan zhihu com p 480357405 https zhuanlan zhihu com p 76249973 Lua 协同程序 cor
  • Lua 变量

    Lua 变量 参考至菜鸟教程 变量在使用前 xff0c 需要在代码中进行声明 xff0c 即创建该变量 变量需要标识类型是因为编译程序执行代码之前需要知道如何给语句变量开辟存储区 xff0c 用于存储变量的值 Lua变量有三种类型 xff1
  • Lua 函数 - 可变参数

    Lua 函数 可变参数 参考至菜鸟教程 Lua函数可以接收可变数目的参数 xff0c 和C语言类似 xff0c 在函数参数列表中使用三点 表示函数有可变的参数 span class token keyword function span s
  • Lua 运算符 - 较为特殊部分

    Lua 运算符 较为特殊部分 参考至菜鸟教程 算术运算符 操作符描述实例 乘幂A 2 输出结果 100 负号 A 输出结果 10 整除运算符 gt 61 lua5 3 5 2 输出结果 2 在 lua 中 xff0c 用作除法运算 xff0
  • python(9):python循环打印进度条

    1 while 循环 Python的while循环可以打印进度条 xff0c 可以使用tqdm这个库来实现 tqdm是一个用于在Python中添加进度条的库 xff0c 它可以很容易地集成到while循环中 下面是一个简单的示例 xff0c
  • 平衡车直立PID调节总结

    苦战一周 xff0c 终于使平衡小车站了起来 xff0c PID无疑是我从学习板子至今遇到最困难的东西了 xff0c 并不是说它原理有多么复杂 xff0c 只是想让小车的效果更佳 xff0c 调参的过程无疑是漫长而艰难的 连续调了俩天的参数
  • Lua 字符串

    Lua 字符串 参考至菜鸟教程 字符串或串 String 是由数字 字母 下划线组成的一串字符 Lua 语言中字符串可以使用以下三种方式来表示 xff1a 单引号间的一串字符 双引号间的一串字符 与 间的一串字符 以上三种方式的字符串实例如
  • Lua 迭代器

    Lua 迭代器 参考文章 xff1a 菜鸟教程 https cloud tencent com developer article 2203215 迭代器 xff08 iterator xff09 是一种对象 xff0c 它能够用来遍历标准
  • Lua table(表)

    Lua table 表 参考至菜鸟教程 Lua table 使用关联型数组 xff0c 你可以用任意类型的值来作数组的索引 xff0c 但这个值不能是 nil Lua table 是不固定大小的 xff0c 你可以根据自己需要进行扩容 Lu
  • Lua 模块与包

    Lua 模块与包 参考至菜鸟教程 模块类似于一个封装库 xff0c 从 Lua 5 1 开始 xff0c Lua 加入了标准的模块管理机制 xff0c 可以把一些公用的代码放在一个文件里 xff0c 以 API 接口的形式在其他地方调用 x
  • Lua 文件I/O

    Lua 文件I O 参考至菜鸟教程 Lua I O 库用于读取和处理文件 分为简单模式 xff08 和C一样 xff09 完全模式 简单模式 xff08 simple model xff09 拥有一个当前输入文件和一个当前输出文件 xff0
  • Lua 错误处理

    Lua 错误处理 参考至菜鸟教程 程序运行中错误处理是必要的 xff0c 在我们进行文件操作 xff0c 数据转移及web service 调用过程中都会出现不可预期的错误 如果不注重错误信息的处理 xff0c 就会造成信息泄露 xff0c
  • Lua 调试(Debug)

    Lua 调试 Debug 参考至菜鸟教程 Lua 提供了 debug 库用于提供创建我们自定义调试器的功能 Lua 本身并未有内置的调试器 xff0c 但很多开发者共享了他们的 Lua 调试器代码 Lua 中 debug 库包含以下函数 x
  • Lua 垃圾回收

    Lua 垃圾回收 参考至菜鸟教程 Lua 采用了自动内存管理 这意味着你不用操心新创建的对象需要的内存如何分配出来 xff0c 也不用考虑在对象不再被使用后怎样释放它们所占用的内存 Lua运行了一个垃圾收集器来收集所有死对象 xff08 即
  • Lua 面向对象(详解)

    Lua 面向对象 xff08 详解 xff09 参考文章 xff1a https blog csdn net linxinfa article details 103254828 https zhuanlan zhihu com p 115