前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Lua面向对象编程的基本原理示例

Lua面向对象编程的基本原理示例

作者头像
IOT物联网小镇
发布2022-09-26 16:10:59
4530
发布2022-09-26 16:10:59
举报
文章被收录于专栏:IOT物联网小镇IOT物联网小镇

作 者:道哥,10+年嵌入式开发老兵,专注于:C/C++、嵌入式、Linux。

目录

  • 一些废话
  • 测试代码
  • 代码说明
    • 基类(父类) A
    • 派生类(子类) B
    • 子类对象操作私有变量
  • 继续往下继承

别人的经验,我们的阶梯!

一些废话

Lua语言是一个小而美的语言,使用者不多。

估计阅读这篇文章的人也不会多,姑且当做一篇笔记吧。

这篇文章主要描述:在Lua语言中,如何通过table结构来实现面向对象编程。

主要是看到某鸟教程上错误百出,估计示例代码自己都没有测试过;

关于Lua语言中的table以及metatable的基本知识,这里就不赘述了,官方手册中描述的很清楚。

测试代码

代码语言:javascript
复制
1 #!/usr/bin/lua
2 
3 ------------------------------ class A
4 A = {
5         a = 1,
6         funcA = function()
7                 print("this is funcA")
8         end
9 }
10 
11 function A:new(t)
12         local t = t or {}
13         self.__index = self
14         setmetatable(t, self)
15         return t
16 end
17 
18 function A:myadd(num)
19         self.a = self.a + num
20 end
21 
22 objA = A:new()
23 print("objA.a = " .. objA.a)
24 print(objA.funcA())
25 print(string.rep("-", 20))
26 
27 ------------------------------ class B
28 B = A:new({
29         b = 2,
30         funcB = function()
31                 print("this is funcB")
32         end
33 })
34 
35 objB = B:new()
36 print("objB.a = " .. objB.a)
37 print("objB.b = " .. objB.b)
38 print(objB.funcA())
39 print(objB.funcB())
40 
41 objB:myadd(10)
42 print("objA.a = " .. objA.a)
43 print("objB.a = " .. objB.a)

执行结果如下:

代码语言:javascript
复制
$ ./oop.lua 
objA.a = 1
this is funcA

--------------------
objB.a = 1
objB.b = 2
this is funcA

this is funcB

objA.a = 1
objB.a = 11

代码说明

基类(父类) A

首先来分析下4-25行的代码。

4-9行:定义父类A的成员变量和函数(按照C++中的习惯,可以叫做方法),可以看出Lua语言中的函数是“一等公民”,是可以赋值给一个变量的。

11-16行:相当于是构造函数,用来创建一个父类A的对象。

18-20行:给父类A增加一个函数,待会在分析子类B的时候再说。

22行:调用A:new()函数,创建一个类A的对象,赋值给变量objA

A:new()函数中,关键是第13行代码:此时self等于A,就相当于是A.__index = A,这是合法的。

因为函数的调用方式是A:new()Lua的语法糖会把A作为第一个参数传递给new()函数的第一个隐藏参数self

然后执行14行的setmetatable(t, self),相当于把表t的元表设置为A

以上两行搞明白之后,23-24行的打印语句就简单了:

23行:因为表objA中没有成员a,但是objA被设置了元表A,而且该元表A带有__index属性,该属性的值是表A自己,于是就到A中查找是否有成员a,于是就打印出:

代码语言:javascript
复制
objA.a = 1

__index 属性的值,可以是一个表,可以是一个函数; 只不过这里特殊一点:__index 设置为 A 自己;

24行:查找函数的过程是一样的,找到元表A__index属性的值,也就是表A自己中的funcA函数,然后调用,打印出:

代码语言:javascript
复制
this is funcA
派生类(子类) B

28-33行:定义了子类B,其实它也是一个对象。

在创建函数A:new(t)中,参数t的值是:

代码语言:javascript
复制
local t = {
    b = 2,
    funcB = function()
        print("this is funcB")
    end
}

此时,self仍然是父类AB的创建过程与objA的创建过程是一样的,只不过给参数t设置了子类B自己的成员变量和函数。

所以,B的元表被设置为A(14行代码的功劳),当然了A__index仍然被设置为A自己。

关键是35行:objB = B:new(),得仔细唠唠。

子类B并没有自己的new函数,但是类B(也是一个 table) 的元表被设置为A,并且A.__index = A,所以最终就找到了A中的new函数,也就是11-16行代码。

进入这个函数中时,第一个隐藏参数self被设置为 B 了,因为函数调用形式是:B:new()

所以:

13 行 self.__index = self 相当于设置 B.__index = B 14 行 etmetatable(t, self) 相当于把表 t 的元表设置为 B

new()函数返回之后,就把t赋值给objB

下面再看一下36-39行的打印语句:

代码语言:javascript
复制
36 print("objB.a = " .. objB.a)
37 print("objB.b = " .. objB.b)
38 print(objB.funcA())
39 print(objB.funcB())

36行:objB中并没有成员a,但是objB的元表是B,而且B.__index = B,所以就到B中去查找a

虽然B中也没有a,但是B的元表是A,而且A.__index = A,所以就在A中找到了成员a,打印出:

代码语言:javascript
复制
objB.a = 1

37行:objB中并没有成员b,但是objB的元表是B,而且B.__index = B,所以在B中找到了成员b,因此打印出:

代码语言:javascript
复制
objB.b = 2

3738行的查找过程是类似的,只不过换成了函数而已。

子类对象操作自己的变量

41行:objB:myadd(10)

查找myadd函数的过程与查找obj.a的过程是一样的,这里再唠叨一遍:

  1. objB 中并没有函数 myadd,但是 objB 的元表是 B,而且 B.__index = B,所以就到 B 中去查找 myadd;
  2. 虽然 B 中也没有 myadd,但是 B 的元表是 A,而且 A.__index = A,所以就在 A 中找到了函数 myadd;

于是就调用了函数:

代码语言:javascript
复制
18 function A:myadd(num)
19         self.a = self.a + num
20 end

而且self等于objB,因此函数体中就等于是:

代码语言:javascript
复制
objB.a = objB.a + 10

加法表达式中的objB.a的读取过程,上面已经描述过了,最终定位到的是父类A中的a,即:1

1 + 10 = 11,然后把11赋值给objB.a

在赋值操作中,被赋值的objB.a就不再是父类A中的那个a了!

因为objB本质是一个table,给objB设置键值对的时候:

  1. 如果键已经存在了,那么就直接设置该键的值;
  2. 如果键不存在,那么 lua 会看它的元表中是否有 __newindex 字段(可以是一个table,也可以是一个函数);

2-1. 如果有 __newindex 字段,那么就是调用 __newindex (如果是一个函数),或者在 __newindex 中添加键值对(如果是一个table); 2-2. 如果没有 __newindex 字段,那么就直接在 objB 中存储该键值对;

根据上面这个规则,就会设置objB.a = 11

明白以上这些之后,4243行的打印语句就不复杂了。

42行:objA最终找到的a是父类A中的成员a,打印出:objA.a = 1

43行:objB中自己已经有了成员a,所以打印出:objB.a = 11

继续往下继承

有了上面的基础,再从子类B中派生出类CC派生出类D... 都不是什么问题了,如下所示:

代码语言:javascript
复制
C = B:new()
objC = C:new()
print("objC.a = " .. objC.a)
print("objC.b = " .. objC.b)
print(objC.funcA())
print(objC.funcB())

感兴趣的读者可以自己测试一下。

------ End ------

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2022-08-16,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 IOT物联网小镇 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一些废话
  • 测试代码
  • 代码说明
    • 基类(父类) A
      • 派生类(子类) B
        • 子类对象操作自己的变量
        • 继续往下继承
        领券
        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档