Lua从虚拟机原理分析到lua代码实现面向对象过程的理解

本文详细介绍了Lua虚拟机的基础原理,包括基于栈和寄存器的虚拟机对比,强调了Lua作为首个基于寄存器的虚拟机的优势。接着,重点讲解了Lua中的元表(Metatable)概念,包括__index、__newindex、__call、__tostring等元方法,以及它们在实现面向对象编程中的作用。通过示例代码,展示了元表如何改变表的行为,使读者更好地理解和使用Lua进行面向对象编程。
摘要由CSDN通过智能技术生成

希望能帮助到那些lua的初学者,更好的理解和使用lua这门语言

 

lua作为一个使用寄存器虚拟机的语言,他需要实现如下几个部分:

 

1.    将源代码编译程虚拟机可识别执行的字节码

2.    为函数准备调用栈

3.    内部维持一个IP(指令指针)来保存下一个将执行的指令地址

4.    模拟一个cpu的运行,循环拿出ip指向的字节码,根据字节码进行解码,然后执行字节码

 

 

虚拟机有两种不同的实现方式,基于栈的虚拟机和基于寄存器的虚拟机,lua是第一个基于寄存器的虚拟机,那么我会讲一下这两种虚拟机的不同,并做一下分析

 

基于栈的虚拟机:

在基于栈的虚拟机中,字节码的操作数在栈顶弹出,在执行完成之后再压入栈顶的,在我们的内存中,栈作为一个独立的内存区域,我们去计算一个数据的时候,基于栈的虚拟机,会首先把需要的数据压入栈顶,在计算完结果后,把之前压入栈顶的数据push掉后,把结果在压入栈顶,这个过程在计算一个类似1+2=3的方法时,实际需要如下四个步骤:

 

1.    Pop 1

2.    Pop 2

3.    ADD 1,2,result

4.    Push result

 

可以看到,执行一条假发需要四条字节码,前面几条指令用来准备数据,将数据压入栈,这就是设计上的缺陷。但优点是,指令中不需要关心操作数的地址,在执行操作数据已经把数据放在栈顶了

 

基于寄存器的虚拟机

与基于栈的虚拟机不同,在基于寄存器的指令中,操作数是放在“cpu寄存器”中,这个并不是物理意义上的寄存器,因此同样操作不再需要push,pop指令,取而代之的是在字节码中带上具体操作数所在的寄存器地址,对比基于栈的虚拟机,由四条指令降低为一条,但缺点是程序需要关注操作数所在的位置

 

之所以讲lua虚拟机一些基础原理,是为了接下来有助于帮助我们在使用lua时候有更深刻的理解

 

我从15年入行,17年才接触lua,从经验来说,只能算个新兵,同时这是我第一篇博客,我希望以一个新兵的视角给大家在学习lua带来帮助

 

 

Lua语法篇:

lua有很多数据结构

Lua 数据类型

Lua是动态类型语言,变量不要类型定义,只需要为变量赋值。值可以存储在变量中,作为参数传递或结果返回。

Lua中有8个基本类型分别为:nilbooleannumberstringuserdatafunctionthreadtable

数据类型

描述

nil

这个最简单,只有值nil属于该类,表示一个无效值(在条件表达式中相当于false)。

boolean

包含两个值:falsetrue

number

表示双精度类型的实浮点数

string

字符串由一对双引号或单引号来表示

function

C Lua 编写的函数

userdata

表示任意存储在变量中的C数据结构

thread

表示执行的独立线路,用于执行协同程序

table

Lua 中的表(table)其实是一个"关联数组"associative arrays),数组的索引可以是数字或者是字符串。在 Lua 里,table 的创建是通过"构造表达式"来完成,最简单构造表达式是{},用来创建一个空表。

 

 

以上是

https://www.runoob.com/lua/lua-data-types.html  摘抄的引用

这里的userdata,你可以理解为一个内存地址,实际上对于lua虚拟机,table实际上也是一块内存地址,那么我谈一谈table这种数据结构吧,table里面的key有两种类型,一种是int类型,一种是其他类型,这篇博客会讲述实现原理:

https://blog.csdn.net/zr339361504/article/details/52432163

 

 

 

理解完lua table的实现,我们聊一聊我们常见的:和.这两个标识符,我引用网上一篇博客做解释

1.    原文地址https://www.cnblogs.com/xzxdm/p/6980267.html

lua的语法很简单,太简单了,所以lua就复杂了

两个点与一个点有什么区别呢?

一个点用来定义和取得一个变量,这个变量可能是个函数

两个点用来定义和调用一个函数,两个点会自动传入调用者这个table自身

先看简单的:

c = {a = 1, b = 2}

function c:foo()

print(self.a,self.b)

end

这里,c这个table定义了一个foo函数,用两个点定义的,在foo函数第一个参数的地方自动传入了c自身,然后调用一下

c:foo()

输出1 2

也可以这样,一个点调用函数,手动传入tablec.foo(c)

输出也是一样的

 

下面看复杂一点的:

两个点会自动传入调用者本身,但是他仅仅只会传入调用者本身,他没有一个点灵活,这个怎么理解?看代码

c = {a = 1, b = 2}

c.foo = function(c) print(c.a,c.b) end

d = {}

d.foo2 = c.foo

d:foo2()

输出为:nil nil

解释下上面的代码:

首先创建一个table,然后定义一个函数foo,这个函数访问了table中的ab两个变量

然后创建了另一个tableb

b中定义一个变量foo2,赋值为c这个table中的foo函数

然后调用d这个table中的foo2函数,输出为nil nil

因为d是两个点调用的foo2,那么会自动吧d本身传入函数的第一个参数,那么函数的运行就是这样的:

function (d)

print(d.a,d.b)

end

d<

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值