Lua面向对象编程学习笔记

其实 Lua 中的 table 是一种对象,因为它跟对象一样,有其自己的操作方法:

代码如下:

Role = { hp = 100 }
function Role.addHp(hp)
    Role.hp = Role.hp + hp
end
 
Role.addHp(50)
print(Role.hp)

上面代码创建了一个名为 Role 对象,并有一个 addHp 的方法,执行 "Role.addHp" 便可调用 addHp 方法。

不过上面对象 Role 是以全局变量的方式创建,会有一种“全局污染”的威胁,即变量 Role 在其他地方被重新赋值(例如被赋成 nil),对象里的属性或方法可能会面临被销毁或不能正常工作的情况。

对于这种问题,Lua 提供一种“接受者”的解决方法,即额外添加一个参数 self 来表示对象本身:

代码如下:

Role = { hp = 100 }
function Role.addHP(self, hp)
    self.hp = self.hp + hp
end
r = Role
r.addHP(r, 50)
print(r.hp)

这样就不怕对象 Role 被“全局污染”,因为构造了一个子对象 r,并以参数的方式传入,以供其方法调用操作。

对于这种把对象本身以参数的方式传入对象方法里的写法,Lua 提供了一种更优雅的写法,把点号(.)替换为冒号(:),这样在方法定义或调用时,便可隐藏 self 参数。修改如下:

代码如下:

Role = { hp = 100 }
function Role:addHp(hp)
    self.hp = self.hp + hp
end
r = Role
r:addHp(50)
print(r.hp)

上面的 "r.addHp(50)" 的写法等价于 "r.addHp(r, 50)"

Lua 没有类的概念,不过可以通过元表(metatable)来实现与原型 prototype 类似的功能,而 prototype 与类的工作机制一样,都是定义了特定对象行为。Lua 里的原型特性主要使用元表的 __index 事件来实现,这样当调用对象没定义的方法时,会向其元表的 __index 键(事件)查找。例如有 a 和 b 两个对象,想让 b 作为 a 的原型 prototype,只需要把 b 设置为 a 元表的 __index 值就行:

代码如下:

setmetatable(a, {__index = b})

这样,当对象 a 调用任何不存在的成员都会到对象 b 中查找,a 可以拥有或调用 b 的属性或方法,从某种意义上看,b 可以看作是一个类,a 是 b 的对象。

对于上面 Role 的例子,对象的创建可以用 __index 元方法来改写,这样新创建的对象就拥有和 Role 一样的属性和方法。

代码如下:

function Role:new(o)
    o = o or {} 
    setmetatable(o, self)
    self.__index = self
    return o
end

当执行 "r = Role:new() " 创建一个对象时,r 将 Role 设置为自己的元表,那么调用 "r:addHp(50)" 的时候,会在 r 里查找 addHp 方法,如果没有找到,则会进一步搜索其元表的 __index,因此等价于:

代码如下:

getmetatable(r).__index.addHp(r, 50)

从上面的 Role:new 方法可以知道,Role 的 __index 在创建时被指定为 self,因此其实就是执行:

代码如下:

Role.addHp(R, 50)

完整的类例子:

代码如下:

Role = { hp = 100 }
function Role:new(o)
    o = o or {} 
    setmetatable(o, self)
    self.__index = self
    return o
end
function Role:addHp(hp)
    self.hp = self.hp + hp
end
 
r = Role:new()
r:addHp(50)
print(r.hp)

继承

Lua 里继承机制还是像实现类那样实现。

假如打算从类 Role 派生出一个子类 Priest,它有一个魔法属性值 mp,那么可以先从类 Role 构造一个 Priest,继承类 Role 的所有属性和方法:

代码如下:

Priest = Role:new()

虽然 Priest 是 Role 的一个实例,不过它具有类 Role 的所有属性和方法,其实也可以把它看做是从类 Role 派生出来的类,因此可以从类 Priest 继续 new 一个对象出来:

代码如下:

p = Priest:new({ mp = 100 })

上面实例 p 除了多出一个魔法属性值 mp 外,还继承类 Role 的所有属性和方法,当调用 "p.addHp" 方法时,Lua 在 p 中找不到 addHp 方法,会到 Priest 中找,在 Priest 中找不到,会到 Role 中找。

因此,想重定义从父类 Role 继承来的方法,在类 Priest 上定义即可。假如想重定义 addHp 方法:每次加血都要先判断魔法值够不够,如果够,则加血,并扣除一定的魔法值。修改如下:

代码如下:

function Priest:addHp(hp)
    if self.mp >= 20 then
        self.mp = self.mp - 20
        self.hp = self.hp + hp
    end
end

这样,当调用 "p:addHp" 时,Lua 会优化取类 Priest 定义的 addHp 方法。

(0)

相关推荐

  • Lua字符串库中的几个重点函数介绍

    在<Lua中的一些库>中也说到了,要对string库的模式匹配进行单独的讲解.对于字符串的处理,对于任何语言的学习来说,都是一个难点,而且也是一个必会的知识点.给你一个字符串,让你按照某种需求进行处理,你不会,那是多么尴尬的一件事情.所以,看完<Lua中的一些库>和这篇文章之后,我争取做到让你在处理字符串时,不再感到捉襟见肘,不再尴尬. 说到Lua中的模式匹配,基本上就是围绕着以下几个函数展开的: 1.find: 2.match: 3.gsub: 4.gmatch. 我的总结也就是

  • Lua中函数与面向对象编程的基础知识整理

    函数 1. 基础知识 调用函数都需要写圆括号,即使没有参数,但有一种特殊例外:函数若只有一个参数且参数是字面字符串或table构造式,则圆括号可有可无,如dofile 'a.lua',f{x=10, y=20}. Lua为面向对象式的调用提供冒号操作符的特殊语法,如o.foo(o, x)等价于o:foo(x).和Javascript类似,调用函数时提供的实参数量可以与形参数量不同,若实参多了则舍弃,不足则多余的形参初始化为nil. 1.1 多重返回值 Lua允许函数返回多个结果,函数返回如ret

  • Lua面向对象之类和继承

    终于来了,在Lua中的面向对象编程,相信目前学习Lua的大部分人都是为了开发手机网游吧. 而且基本都是奔着脚本语言的热更新特性去的,所以全脚本开发变得十分流行. 对于普及不太广的Lua(相对于C++.Java等主流语言),需要短时间上手开发游戏,对新手而言不算简单. 所以大家才更习惯于继续用面向对象思想去折腾Lua吧~ 好了,不唠叨了,我最不喜欢唠叨了.(小若:是是是,你一点都不唠叨,赶紧开讲!) 1.类的对象 至于如何创建一个类,大家已经很清楚了,就是一个table而已. 那么,要使用这个类去

  • Lua中的常用函数库汇总

    lua库函数 这些函数都是Lua编程语言的一部分, 点击这里了解更多. assert(value) - 检查一个值是否为非nil, 若不是则(如果在wow.exe打开调试命令)显示对话框以及输出错误调试信息 collectgarbage() - 垃圾收集器. (新增于1.10.1) date(format, time) - 返回当前用户机器上的时间. error("error message",level) - 发生错误时,输出一条定义的错误信息.使用pcall() (见下面)捕捉错误

  • Lua中的函数知识总结

    前言 Lua中的函数和C++中的函数的含义是一致的,Lua中的函数格式如下: 复制代码 代码如下: function MyFunc(param)      -- Do something end 在调用函数时,也需要将对应的参数放在一对圆括号中,即使调用函数时没有参数,也必须写出一对空括号.对于这个规则只有一种特殊的例外情况:一个函数若只有一个参数,并且此参数是一个字符串或table构造式,那么圆括号便可以省略掉.看以下代码: 复制代码 代码如下: print "Hello World"

  • Lua中的面向对象编程详解

    简单说说Lua中的面向对象 Lua中的table就是一种对象,看以下一段简单的代码: 复制代码 代码如下: local tb1 = {a = 1, b = 2} local tb2 = {a = 1, b = 2} local tb3 = tb1   if tb1 == tb2 then      print("tb1 == tb2") else      print("tb1 ~= tb2") end   tb3.a = 3 print(tb1.a) 上述代码会输

  • Lua的table库函数insert、remove、concat、sort详细介绍

    函数列表: table.insert(table,[ pos,] value) table.remove(table[, pos]) table.concat(table[, sep[, i[, j]]]) table.sort(table[, comp]) 1. insert 和 remove 只能用于数组元素的插入和移出, 进行插入和移出时,会将后面的元素对齐起来. 所以在 for 循环中进行 insert 和 remove 的时候要注意插入和移除时是否漏掉了某些项:   复制代码 代码如下

  • Lua中的一些常用函数库实例讲解

    前言 这篇文章将会来一些比较轻松的内容,就是简单的介绍一下Lua中几个常用的库.简单的说就是几个API的介绍.所以说,看起来比较容易,也没有多大的分量.就是纯粹的总结.使用库就是为了方便我们的开发,提高开发效率,同时也能保证代码的质量.希望大家以后也不要重复造轮子了. 数学库 数学库(math)由一组标准的数学函数构成.这里主要介绍几个常用的函数,其它的大家可以自行百度解决. 三角函数(sin,cos,tan--) 所有的三角函数都使用弧度单位,可以用函数deg(角度)和rad(弧度)来转换角度

  • Lua面向对象之多重继承、私密性详解

    在Lua中的多重继承和私密性可能用得比较少,也可能只是我个人用得比较少. 本来想偷懒不写这文章的,因为我今天刚买了个漂移板,连起步都还没学会啊,想多学一会. 咳咳,本着坚持不懈.负责到底的态度,我还是决定随便写几句~(小若:随便写几句是几吨意思啊?!) 1.多重继承之在多个类中查找一个字段 我发现这些高(shen)智(jing)商(bing)人群真的很厉害,这种技巧都能想到,很佩服. 其实多重继承没什么特别的,除非两个将要被继承的类有相同的函数名和属性,否则,处理起来很简单.   无非就是在多个

  • Lua中的函数(function)、可变参数、局部函数、尾递归优化等实例讲解

    一.函数 在Lua中,函数是作为"第一类值"(First-Class Value),这表示函数可以存储在变量中,可以通过参数传递给其他函数,或者作为函数的返回值(类比C/C++中的函数指针),这种特性使Lua具有极大的灵活性.   Lua对函数式编程提供了良好的支持,可以支持嵌套函数.   另外,Lua既可以调用Lua编写的函数,还可以调用C语言编写的函数(Lua所有的标准库都是C语言写的).   定义一个函数 复制代码 代码如下: function hello() print('he

  • Lua中的模块与module函数详解

    很快就要开始介绍Lua里的"面向对象"了,在此之前,我们先来了解一下Lua的模块. 1.编写一个简单的模块 Lua的模块是什么东西呢?通常我们可以理解为是一个table,这个table里有一些变量.一些函数- 等等,这不就是我们所熟悉的类吗? 没错,和类很像(实际上我说不出它们的区别).   我们来看看一个简单的模块,新建一个文件,命名为game.lua,代码如下: 复制代码 代码如下: game = {} function game.play()     print("那么

  • Lua中的string库(字符串函数库)总结

    Lua解释器对字符串的支持很有限.一个程序可以创建字符串并连接字符串,但不能截取子串,检查字符串的大小,检测字符串的内容.在Lua中操纵字符串的功能基本来自于string库. 字符串库中的一些函数是非常简单的: string.len(s)          返回字符串s的长度: string.rep(s, n)      返回重复n次字符串s的串:你使用string.rep("a", 2^20)可以创建一个1M bytes的字符串(比如,为了测试需要): string.lower(s)

随机推荐