Lua面向对象程序设计



Lua面向对象程序设计

Lua中的表某种意义上可以说是一种对象。如如下代码:

    local TA = {a = 1, b = 1}
    local TB = {a = 1, b = 1}
    local TC = TA
    if TA == TB then print("TA == TB")
    else print("TA ~= TB")
    end

    print(TC.a)
    TC.a = 3
    print(TC.a)

运行得到结果为TA ~= TB,由此我们可以知道虽然TA、TB具有相同的值,但他们是不同的对象。由print(TC.a)为3可知道Lua中的table为引用类型。

接着看另一段Lua代码:

    Account = {blance = 0}
    function Account.withDraw(v)
	Account.blance = Account.blance - v
    end
<pre style="margin: 4px 0px; line-height: 21px; background-color: rgb(240, 240, 240);" name="code">
Account.withDraw(10) print(Account.blance) --体会下blance的作用
 
这个定义了创建了一个新的函数,Account.withDraw(10)则是我们的调用方法。然而这种做法的弊端则是函数内部使用全局变量Account,如果全局变量的名称发生了变化,那么调用方法也要做出相应的改变。比如: 

    a = Account
    Account = nil
    a.withDraw(10)
    print(a.blance)
结果会出现错误,我们使用Account创建了一个对象a,当Account = nil时对a并没有什么影响,然而由于函数withDraw在内部使用了Account而不是a,所以会导致出错。需将函数内部改为a.blance = aa.blance - v才可以。所以我们可以发现当我们对函数进行操作时,需要指定的实际的操作对象。所以在定义函数时,我们可以定义一个额外的参数,来表示方法作用的对象,就想是C++中的this,而Lua中通常用self来表示:

<pre style="margin: 4px 0px; line-height: 21px; background-color: rgb(240, 240, 240);" name="code">    Account = {blance = 0}
    function Account.withDraw(self,v)  --等同于 Account:withDraw(v) 即可以把self省略,注意换成了冒号
	self.blance = self.blance - v
    end
    a = Account
    Account = nil
    a.withDraw(a, 10)    --等同于a:withDraw(10)

    print(a.blance)
 

Lua中的类更贴切的叫法应该为原型,在C++中,类的对象应进行实例化(除了静态成员),然而在Lua中,类可以不进行实例化。在Lua中,要表示一个类,只需创建一个专做其他对象的原型,也就是说我们可以直接通过原型去调用对应的方法。当对象没有找到对应的操作时,则会在原型中进行查找。

在Lua中,原型的实现非常简单,比如有两个对象a和b,如要要b作为a的原型,则有

setmetatable(a,{__index = b})


设置了这段代码后,a便会在b中才找它没有的操作,某种意义上说b为a的类。要创建一个实例对象,必须要有一个原型,如:

local Account= {} --创建一个原型


对原型进行实例化:

function Account:new(o)
    o = o or {} --如果为nil table,给o创建一个
    setmetatable(o,self)  --Account作为o的原型
    self.__index = self
    return o
end

当调用Account时,相当于调用self:

local a = Account:new(value = 100) --使用原型Account创建了一个对象a

现在我们对该原型进行修改:

local Account = {value = 0}
function Account:new(o)
    o = o or {} --如果为nil table,给o创建一个
    setmetatable(o,self)  --Account作为o的原型
    self.__index = self
    return o
end
function Account:display()
    self.value = self.value + 100
    print(self.value)
end

local a = Account:new()
a:display()
a:display()


输出结果为100,200.当我创建了实例对象a时,并没有提供value字段,在display函数中,由于a中没有value字段,就会查找元表Account,最终得到了Account中value的值,等号右边的self.value的值就来源自Account中的value。调用a:display()时,其实就调用以下代码:

a.display(a)

在display中定义则变成了:

a.value = getmetatable(a).__index(value) + 100

第一次调用display时,等号左侧的self.value就是a.value,就相当于在a中添加了一个新的字段value;当第二次调用display函数时,由于a中已经有了value字段,所以就不会去Account中寻找value字段了。

继承

沿用上面代码,稍微修改后得:

local Account = {value = 0}
function Account:new(o)
    o = o or {} --如果为nil table,给o创建一个
    setmetatable(o,self)  --Account作为o的原型
    self.__index = self
    return o
end
function Account:display(v)
    self.value = self.value + v
    print(self.value)
end


现从类Account派生出一个子类ASon:

local ASon = Account:new()


ASon既是一个类,又是一个对象,相当于ASon继承Account,再如如下代码:

local s = ASon:new{value1 = 10}

ASon从Account继承了new;不过,在执行ASon:new时,它的self参数表示为ASon,所以s的元表为ASon,ASon中字段__index的值也是ASon。然后,我们就会看到,s继承自ASon,而ASon又继承自Account。当执行s:display时,Lua在s中找不到display字段,就会查找ASon;如果仍然找不到display字段,就查找Account,最终会在Account中找到display字段。可以这样想一下,如果在ASon中存在了display字段,那么就不会去Account中再找了。所以,我们就可以在ASon中重定义display字段,从而实现特殊版本的display函数。


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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值