前言
一些感觉不足以单独开一篇文章写的lua相关内容,我都会尽量的放在这里。
不止是八股,也会有一些平常学习的小心得。
目前有点乱,以后内容补充多了会整理切分
Lua语言相关
Lua的基础数据类型有哪些
基础数据类型的使用
nil
- 用途:表示一个变量没有任何值。
- 示例:
local x = nil
if x == nil then
print("x is nil")
end
boolean
- 用途:表示布尔值,用于条件判断。
- 示例:
local enabled = false
if enabled then
print("Feature is enabled")
else
print("Feature is disabled")
end
number
- 用途:表示数值,可以是整数或浮点数。
- 示例:
local num = 10
local result = num * 2
print(result) -- 输出 20
- 用途:表示文本字符串,用于字符串操作。
- 示例:
local str = "Hello, World!"
print(str) -- 输出 Hello, World!
print(string.len(str)) -- 输出 13
table
- 用途:表示复合数据类型,可以当作数组或哈希表使用。
- 示例:
local table = {"apple", "banana", "cherry"}
for i, fruit in ipairs(table) do
print(i .. ": " .. fruit)
end
-- 输出
-- 1: apple
-- 2: banana
-- 3: cherry
function
- 用途:表示可调用的对象,即函数。
- 示例:
local add = function(a, b)
return a b
end
print(add(1, 2)) -- 输出 3
userdata
- 用途:表示用户定义的数据类型,通常用于存储 C 语言中的指针或结构体。
- 示例:
local ptr = ffi.new("int[10]")
ptr[0] = 42
print(ptr[0]) -- 输出 42
thread
- 用途:表示协程(coroutine),即轻量级的线程。
- 示例:
local co = coroutine.create(function()
print("Inside coroutine")
end)
coroutine.resume(co) -- 输出 "Inside coroutine"
常见操作
1. 类型检查
- 用途:检查变量的类型。
- 示例:
local x = "hello"
if type(x) == "string" then
print(x .. " is a string")
end
2. 类型转换
- 用途:将一种类型转换为另一种类型。
- 示例:
local num = tonumber("123") -- string类型转为number类型
print(num 1) -- 输出 124
Lua的面向对象怎么做
在 Lua 中,面向对象编程主要是通过元表(metatable)和表(table)来实现的。Lua 不像其他语言那样有类的概念,而是使用表来模仿类的行为。
对象的概念
在 Lua 中,一个表(table)可以被视为一个对象。每个对象都有自己的状态(数据成员)和行为(成员函数)。
对象实现思路
封装
封装是指将数据和操作数据的方法绑定在一起,隐藏对象的具体实现细节。在这个过程中,我们定义一个类(即原型对象),从这个类派生出具体的对象。每个具体的对象都有自己的状态(成员变量),并通过成员函数来操作这些状态。
为了实现这一过程,我们可以将原型对象作为具体对象的元表(metatable),这样当具体对象找不到某个方法时,就会去原型对象中查找。此外,具体对象通过 self 来在成员函数中调用自身的成员变量。
继承
继承允许子类继承父类的特性和行为。在这个过程中,我们同样可以从父类的类对象派生出子类的类对象。子类可以覆盖或扩展父类的行为。
当子类定义了与父类同名的函数时,由于子类可以直接找到这些函数,因此不会去父类中查找,这就导致了子类会隐藏父类的同名函数。
多重继承
多重继承允许一个子类继承多个父类的功能。在实现多重继承时,我们需要保存传递过来的所有父类对象,并形成一个父类列表。
然后,我们可以设置子类的元表的 __index 属性为一个查找函数。这个查找函数会在父类列表中遍历,寻找相应的字段或方法。
私有性
私有性是指只有对象自身可以访问其内部状态,外部代码无法直接访问这些状态。为了实现私有性,我们可以利用局部变量和闭包。
具体来说,可以创建一个表来保存私有变量,另一个表来保存公共的字段和接口函数。内部的公共函数通过闭包来访问私有成员变量,并将包含公共接口的表返回出去。
详细实现可参考笔者的另一篇文章
Lua学习笔记:实现一个Lua Class生成器
__index和 __newindex
- __index 是一个特殊的元方法,当尝试访问一个表中不存在的键时,Lua 会调用这个方法。这个方法可以用来提供默认值或者实现继承行为。
- __newindex 也是一个特殊的元方法,当尝试向表中添加一个新的键或更新一个已存在的键时,Lua 会调用这个方法。这个方法可以用来拦截对表的修改操作,从而实现只读表或者其他自定义的行为。
Lua的只读表怎么实现
要创建一个只读表,可以使用元表中的 __newindex 方法来阻止对表的任何修改:
代码语言:lua复制深色版本
local ro_table = setmetatable({}, {
__newindex = function()
error("Attempt to modify a read-only table")
end
})
ro_table.x = 1 -- 这将抛出错误
全局禁用
代码语言:lua复制-- 设置全局表的元表 禁止创造全局变量
setmetatable(_G, {
__newindex = function(t, key, value)
print("Cannot create global variable '" .. key .. "'")
end
})
Lua与C交互(栈)
Lua 与 C 的交互主要通过 Lua 的 C API 实现。在 Lua 中,所有的值都是放在栈上的。Lua 的 C API 提供了一系列的函数来从 C 调用 Lua 代码,并从 Lua 调用 C 函数。
GC(四个阶段,标记清扫最主要)
Lua 使用引用计数和周期性的全局扫描(mark-and-sweep)来实现垃圾回收。当一个值不再被引用时,它会被回收。GC 过程包括几个阶段:
- 标记(Marking):标记所有活动的对象。
- 清扫(Sweeping):清理未被标记的对象。
- 重置(Resetting):重置引用计数。
- 最终化(Finalization):调用对象的 __gc 元方法(如果存在的话)。
GC过程中的黑白灰三状态颜色标记
在 Lua 的垃圾回收(GC)过程中,对象的状态通常被分为三类:黑色(black)、灰色(gray)和白色(white)。这些颜色用来表示对象的状态,以便进行标记-清扫(mark-and-sweep)算法的实现。下面详细解释每种颜色的意义:
- 黑色(Black) 黑色对象是指已经被标记为可达(reachable)的对象。这些对象不会被垃圾回收器回收。在标记阶段,从根集开始,递归地标记所有可达的对象为黑色。
- 灰色(Gray) 灰色对象是指正在被处理的对象。这些对象已经标记为可达,但是它们指向的其他对象还没有被标记。因此,灰色对象是处于标记过程中的对象。当一个对象被标记为灰色之后,它需要处理完它所指向的所有对象,才能变为黑色。
- 白色(White) 白色对象是指尚未被标记的对象。在垃圾回收开始时,所有的对象都是白色的。如果一个对象在整个标记过程中都没有被标记(即保持白色),那么这个对象被认为是不可达的,最终会被回收。
标记-清扫算法是 Lua 中垃圾回收的基本算法。它分为两个主要阶段:
标记(Marking):
- 从根集(root set)开始,这些根集通常包括全局变量、活动函数的局部变量和其他活跃对象。
- 递归地标记所有从根集可达的对象为黑色或灰色。
- 如果一个对象被标记为灰色,则标记它所指向的所有对象。 当一个对象的所有引用都被处理完毕后,它就变成了黑色。 清扫(Sweeping):
- 清除所有未被标记的对象(即白色对象),因为它们是不可达的。
- 释放这些对象占用的内存,并将它们从数据结构中移除。
table的内部结构实现(数组部分和hash部分)遍历方法(3种各自的区别)
Lua 的 table 结构实际上是由一个数组部分和一个哈希表部分组成的混合结构。数组部分用于快速访问连续的索引(通常是数字),而哈希表部分用于非连续的索引或其他类型的键。
遍历方法
Lua 中有三种常见的遍历表的方法:
- pairs pairs 返回一个迭代器函数、表和一个初始索引。 适用于遍历数组部分和哈希部分的所有元素。for k, v in pairs(my_table) do print(k, v) end
- ipairs ipairs 只遍历数组部分的元素,并按索引顺序返回。 适用于遍历具有连续数字索引的表。
for i, v in ipairs(my_table) do
print(i, v)
end
next
next 函数手动遍历表。
需要显式地管理迭代状态。
代码语言:lua复制local k, v = next(my_table)
while k do
print(k, v)
k, v = next(my_table, k)
end
字符串是如何存的(只存一份)
Lua 中的字符串一旦创建就不会改变,并且相同内容的字符串在内存中只会保存一份。这意味着如果你有两个字符串字面量 "hello",它们实际上指向同一个内存地址。
Lua和C/C 相互传递的变量是如何防止被GC的
当 Lua 中的值被传递给 C 函数时,可以使用 luaL_ref 来保存这个值的引用,从而防止它被垃圾回收。当不再需要这个值时,使用 luaL_unref 释放引用。
闭包
闭包是一个函数与其相关的引用环境组合而成的一个整体。在 Lua 中,闭包允许函数访问其外部作用域中的变量,即使该函数在其定义的作用域之外被调用也是如此。
代码语言:lua复制local x = 10
local function f()
print(x)
end
x = 20
f() -- 输出 20
在这个例子中,f 是一个闭包,因为它记住了定义它的上下文中 x 的值。即使 x 的值后来改变了,f 仍然可以访问到原始的 x 的值。
尾声
一开始只是想做一个面试碰到,或者网上面经总结的Lua八股笔记,在腾讯云开发者社区写习惯以后在别的地方写文章总觉得有点别扭,于是干脆把本地写的内容放在腾讯云社区。
以后再碰到相关八股内容也会补充到这篇文章
并且尽量让内容不止于八股,而是可以从八股衍生出去思考。
当然笔者的水平有限,答案以参考lua手册和ai回答以及网上文章得出,其中纰漏谬误难以避免,若能指出,不胜感激。
若有侵权也可联系笔者指出删除(⊙﹏⊙)