Lua学习摘记

 之前都是从事3D仿真类的工作,最近想找投身游戏行业,看了看各个手游公司的要求,发现几乎都需要掌握Lua这门用于热更新的语言,其实之前第一家公司用过Lua语言,当时只是简单学习了一下,用到了一个MoonSharp的Lua与C#交互的框架,只是用作更新一个小模块的功能,学的不深,最近开始系统的学习Lua,并且研究腾讯开元的ToLua框架,为自己的游戏梦努力!所以将在这里,记录自己的学习笔记。

**

Lua面向对象

**
lua的面向对象实现很有趣,lua的基本数据类型有8个 nil ,number ,string , boolean , function , table , userdata , thread 。其中的table就是实现面向对象的重中之重 ,lua对table的一些机制设计十分巧妙,只需要熟悉这些机制 ,用简洁的代码就能实现面向对象,不得不让我佩服lua语言设计者的厉害!

原表(metatable)与元方法(meatmethod)

每个table都可以有自己的原表,但table创建新的表时并不会创建原表,即其原表为nil

t={}
print(getmetatable(t))--> nil

使用元方法getmetatable,获取其原表,打印输出,结果为nil。可以使用setmetatable的方法给表设置原表

t={}
metatable={}
setmetatable(t,metatable)
print(getmetatable(t))--> table:0x005a11f2

看到输出的结果为原表的地址。
元方法是个非常牛x的机制,我们知道c++ ,c#里有个概念叫重载预算符,就是可以自定义加减乘除的运算符方法,lua也提供了这样的功能,不过lua是靠原表元方法__add(+),__sub(-),__mul(*),__div(/)来实现的,比如我们可以通过这些方法实现两个table进行相加,即求合集的功能

Set={}
local metatable={}

function Set.new(l)
  local set={}
  setmetatable(set,metatable)
  for _,v in ipairs(l) do
    set[v]=true
    end
  return set
end

function Set.union(a,b)
  local res=Set.new({})
  for k in pairs(a) do res[k]=true end
  for k in pairs(b) do res[k]=true end
return res
end

s1=Set.new{1,2,3,4,5}
  s2=Set.new{7,8,9}

  print(getmetatable(s1))
    print(getmetatable(s2))
   metatable.__add=Set.union
    s3=s1+s2
    for v in pairs(s3) do
      print(v)
      end
    print(s3)

输出结果如下:

table: 0x006a19f0
table: 0x006a19f0
1
2
3
4
5
7
8
9
table: 0x006a1ce8

lua也提供了一些关系类的元方法,比如:__eq,__lt,__le,下面来介绍下最重要的一个元方法,__index
lua模拟面向对象,少不了它。
如何实现类?


首先要建立这样的概念,就是每个对象要有一个原型(prototype),当对象的实例遇到一个未知操作时,原型先会去查找这个操作,找到了就回去执行它。如果有两个对象a和b,把b作为a的原型,我们输入如下语句

```bash
setmetatable(a,{__index=b})

那么当a遇到位置操作时就会在b中查找有没有这样的操作。
熟悉面向对象我们知道,一个类包含构造函数,字段,属性,方法,那么我们用下面的代码来模拟一个类

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

function Account.withdraw(self,v)
  self.balance=self.balance-v
end
a=Account:new{balance=10};
a.withdraw(a,1)
print(a.balance)

这段代码里我们用到了__index,它的意思就是设置表的原型索引,这里我们把Account这个table的原表设置为自己,原型索引也设置为自己这个table,分析下代码的工作流程,当我们创建了Account 的实例a,并调用withdraw这个未知操作时,a会先查询自己的原表,发现原表就是Account,那么去执行withdraw方法时,发现自身并没有withdraw这个方法,就会去查找原表里的元方法__index,然后发现Account.__index也是Account,那么就会去调用Account的withdraw方法。这样是不是就实现了类的继承,是不是十分巧妙?如果要实现重载也很简单,那就在a里再写个withdraw方法。

那么如果要实现多重继承,例如类似c#里的接口,怎么做呢?__上例里__index只是给了父类自身,要实现多重继承,我们可以将所有继承的父类存在一张table里并且给这个对象设置一个特殊的原表,__index元方法给一个函数,让其可以在父类里搜索遍历所搜父类同名方法,那么不就实现多重继承了吗?上代码:

    local function search(k,plist)
      for i=1,#plist do
       local v=plist[i][k]
      if v then return v end
    end
  end
  function createClass(...)
    local c={}
    local parents={...}
    setmetatable(c,{__index=function(t,k) return search(k,parents) end})
    c.__index=c
  
  function c:new(o)
    local o=o or {}
    setmetatable(o,c)
    return o
    end
  return c
end
Named={}
function Named: getname()
  return self.name
end

function Named: setname(n)
    self.name=n
  end
  
  Account={}
  
  NameAccount=createClass(Named,Account)
  account=NameAccount:new({name="123"})
  print(account:getname())

分析下这段代码,多重继承的类,它的实例创建是基于多个父类的,那么就需要一个特殊的方法createclass,而且是可变长参数,记录所有父类在parents这个table里,然后给它的__index方法做一个特殊处理,就是在parents中遍历查找基类方法。当我们创建一个继承与Named和Account的实例后,去调用getname方法时自身并没有getname方法,然后它会去找到它的原表和元方法_index,会在parents里遍历找到getname方法,然后去调用。
今天的学习笔记就记录到这~加油!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值