Lua中的面向对象实现(元表,元方法)

简介

1lua面向对象编程是基于元表metatable,元方法__index来实现的,具体元表和元方法的介绍

请见Lua的元表metatable及元方法:

https://blog.csdn.net/sindyra/article/details/103488487

2. 语法糖

语法糖是由英国计算机科学家彼得·约翰·兰达(Peter J. Landin)发明的一个术语,指计算机语言中添加的某种语法,这种语法对语言的功能并没有影响,但是更方便程序员使用

举例说明:数组char test[100],如果要访问第11个元素,可以这样写:char c = *(test+10),但是用语法糖的话就简单了,直接char c = test[10],看到没?语法糖就是一些简便写法

lua中的语法糖

我们有两种函数定义和调用的方法。一种是用属性的方式,另外一种是通过冒号的形式(其实也是属性)。只不过用冒号形式申明的函数默认会有一个参数self。self指向本身(表)

lua中的函数默认都是有self传递进去的,self相当于C++类中函数的this指针,语法糖会自动给我们传递 self

举例说明:

local a =
{
    x = 99
}
 
-- 打印函数,注意这里要访问表a中的变量x,必须指明self.x或者a.x,不然会报错
function a:Print()
    print("function a:test() " ..self.x)
end
 
-- 想调用a的Print()函数,我们可以这样写,注意参数是a,否则调用出错
a.Print(a)
 
-- 也可以这样写,即用:代替. 且不用传入参数a
a:Print()

明显第二种方法更简便

2. lua面向对象的原理(基于元表metatable和元方法__index)

如果访问了lua表中不存在的元素时,就会触发lua的一套查找机制,也是凭借这个机制,才能够实现面向对象的

举例说明:

test =

{

}

-- 访问表test中不存在的变量a

print(test.a)

打印结果:nil

原因很简单:表test中不存在变量a,所以打印为nil,但是如果表test有元表metatable的话,情况就不一样了打印结果:nil

元表像是一个备用查找表,假设表A的元表是B,那么在A中找不到的东西就会尝试在B中去找,设置元表的函数如下

setmetatable(A, B),这样表B就被设置为A的元表,当A中查找不到某个变量时就会到B中进行查找

举例说明:

-- 表A
A =
{
}

-- 表B
B =
{
    a = 99
}


-- 设置表B为表A的元表
setmetatable(A,B)

-- 再访问表A中不存在的变量a
print(A.a)

打印结果依然为:nil
why?因为表B的元方法__index没有赋值。按照笔者的理解,元方法__index是用来确定一个表在被作为元表时的查找方法打印结果依然为:nil

我们做如下更改,即对表B的元方法进行赋值

代码如下:

-- 表A
A =
{
}
 
-- 表B
B =
{
    a = 99
}
 
-- 给表B的元方法__index进行赋值
B.__index = B
 
-- 设置表B为表A的元表
setmetatable(A,B)
 
-- 再访问表A中不存在的变量a
print(A.a)

打印结果:99
查找过程:访问A.a时,表A中没有a这个变量,但是lua发现表A有元表,即表B,于是再到表B中进行查找,但是lua并不是直接在表B中查找变量a,而是调用表B的元方法__index,如果__index为nil,那就会返回nil。如果__index被赋值为一个表(上面的例子就是__index被赋值为表B自己),那么就会到__index指向的那个表(即表B)中进行查询,于是找到了变量a;如果__index被赋值为一个函数,那么查找时就会返回该函数的返回值打印结果:99。

 

总结元表的查找步骤:

步骤1.在表中查找,如果找到,返回该元素,找不到则继续步骤2

步骤2.判断该表是否有元表,如果没有元表,返回nil,有元表则继续步骤3

步骤3.判断元表有没有__index方法,如果__index方法为nil,则返回nil;如果__index方法是一个表,则重复步骤1、2、3;如果__index方法是一个函数,则调用该函数,并返回该函数的返回值

 

3. 面向对象的封装

-- 类Class的声明,其实就是个table,这里有两个成员变量x,y
Class =
{
    x = 1,
    y = 2
}
 
-- 设置metatable的元方法__index,指向表Class自己
Class.__index = Class
 
-- 构造函数,叫什么名字无所谓,这里采用了C++的new名字
function Class:new(x, y)
    print("Class:模拟构造函数new()")
 
    -- 新建一个对象,这样通过Class:new()函数创建的每一个实例都是独立的
    local tempObj = {}
    tempObj.x = x
    tempObj.y = y
 
    -- 设置新对象的metatable,谨记:这一步非常重要
    setmetatable(tempObj,Class)
 
    -- 返回这个新创建的对象
    return tempObj
end
 
-- 类的其他成员函数1
function Class:Print()
    print("Class:Print()")
    print("x = "..self.x..", y = "..self.y)
end
 
-- 类的其他成员函数2
function Class:Add(val)
    print("Class:Add()")
    self.x = self.x + val
    self.y = self.y + val
end
 
-- 类的其他成员函数3
function Class:Modify()
    print("Class:Modify()")
    self.x = 11
    self.y = 22
end
 
-- 下面是测试代码
 
-- 新构造一个类实例
local Obj = Class:new(11,22)
 
-- 调用函数Print()进行打印
Obj:Print()
 
-- 调用函数Add()进行加操作
Obj:Add(5)
 
-- 再次调用函数Print()进行打印,会发现调用Add()函数确实成功了
Obj:Print()
 
-- 做修改
Obj:Modify()
 
-- 再次调用函数Print()进行打印,会发现调用Modify()函数确实成功了
Obj:Print()
 
-- 这里打印出Class本身的数据,会发现数据没有改动,说明是新建的类实例互不影响
print("Class Class.x = "..Class.x..", Class.y = "..Class.y)


测试结果如下:

\

4.面向对象的继承多态

--------------------------------------   基类Class    ------------------------------------------
 
-- 类Class的声明,其实就是个table,这里有两个成员变量x,y
Class =
{
    x = 0,
    y = 0
}
 
-- 设置metatable的元方法__index,指向表Class自己
Class.__index = Class
 
-- 构造函数,叫什么名字无所谓,这里采用了C++的new名字
function Class:new(x, y)
    print("Class:模拟构造函数")
 
    -- 新建一个对象,这样通过Class:new()函数创建的每一个实例都是独立的
    local tempObj = {}
    tempObj.x = x
    tempObj.y = y
 
    -- 设置新对象的metatable,谨记:这一步非常重要
    setmetatable(tempObj,Class)
 
    -- 返回这个新创建的对象
    return tempObj
end
 
 
-- 类的其他成员函数1
function Class:Print()
    print("Class:Print() x = "..self.x..", y = "..self.y)
end
 
-- 类的其他成员函数2
function Class:Add(val)
    print("Class:Add()")
    self.x = self.x + val
    self.y = self.y + val
end
 
-- 类的其他成员函数3
function Class:Modify()
    print("Class:Modify()")
    self.x = 111
    self.y = 222
end
 
--------------------------------------   子类SubClass    ---------------------------------------
 
-- 子类SubClass的声明,这里又声明了一个新的变量z
SubClass =
{
    z = 0
}
 
-- 设置元表为Class
setmetatable(SubClass, Class)
 
-- 设置metatable的元方法__index,指向表SubClass自己
SubClass.__index = SubClass
 
 
-- 构造函数
function SubClass:new(x,y,z)
 
    print("模拟构造函数:SubClass")
 
    -- 先调用父类的构造函数,构造出一个父类的实例
    local tempObj = Class:new(x,y)
 
    -- 将该对象的元表指向SubClass,谨记:这步非常重要,一定不要弄错了,是SubClass
    setmetatable(tempObj,SubClass)
 
    -- 新属性z赋值,有了子类自己的数据,这样就是子类实例了
    tempObj.z = z
 
    return tempObj
end
 
 
-- 定义一个新的成员函数
function SubClass:SubPrint()
    print("SubClass:SubPrint() x = "..self.x..", y = "..self.y..", z = "..self.z)
end
 
-- 重定义父类的函数Add(),注意:和父类的不同,这里是增加了2倍的val
function SubClass:Add(val)
    print("SubClass:Add()")
    self.x = self.x + 2*val
    self.y = self.y + 2*val
end
 
 
-------------------------------------    下面是测试代码      -----------------------------------
 
-- 构造一个基类实例
local Obj = Class:new(11,22)
 
-- 调用函数Print()进行打印
Obj:Print()
 
-- 调用函数Add()进行加操作
Obj:Add(5)
 
-- 再次调用函数Print()进行打印,会发现调用Add()函数确实成功了
Obj:Print()
 
-- 做修改
Obj:Modify()
 
-- 再次调用函数Print()进行打印,会发现调用Modify()函数确实成功了
Obj:Print()
 
-- 这里打印出Class本身的数据,会发现数据没有改动,说明是新建的类实例互不影响
print("Class Class.x = "..Class.x..", Class.y = "..Class.y)
 
 
print("\n")
 
-- 构造一个子类实例
local SubObj = SubClass:new(1,2,3)
 
-- 访问父类的函数
SubObj:Print()
 
-- 访问子类自己的函数
SubObj:SubPrint()
 
-- 调用Add(),这里会发现实际调用的是子类的Add()函数,即实现了多态
SubObj:Add(5)
 
-- 再次调用自己的函数,会发现调用自己的Add()函数确实成功了
SubObj:SubPrint()


测试结果如下:

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值