【Lua】常见知识点汇总(包含常见面试考点)

版权声明:本文为CSDN博主「趁着头发多我想做游戏」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/weixin_37658157/article/details/123604068


1. 如何在Lua中实现一个私有变量或者函数

思路:Lua中并没有private或者public这样的关键字,如果希望某些字段或者函数变为私有,只需要不暴露这些字段或者函数出去即可。

代码如下:

-- Test.lua

local v = {
    
    }
v.x = 100
v.y = 200

function v:new()
    local ins = {
    
    };             --实例化一个对象ins
    setmetatable(ins, self);    --访问对象ins时会去找v里面的字段和方法
    self.__index = {
    
                --对外提供f函数以及字段x、y,注意g函数没有暴露出去
        f = self.f,
        x = self.x,
        y = self.y
    }
    return ins
end

function v:f()
    return v:g()
end

function v:g()
    return self.x + self.y
end

return v
-- Main.lua
local t = require("Test")
local ins = t:new()
local a = ins:f()
print("访问公共函数",a)
local c = ins:g()
print("访问私有函数",c)

运行结果:

访问公共函数	300
attempt to call a nil value (method 'g')

由于函数g并没有被导出(即函数g是一个私有函数),因此访问就会出错。私有字段也是同理,不再赘述。注意,不一定要用元表的方式,也可以加载一个类时返回一个临时表,如:

return {
    
    f = f, x = x, y = y}

2. loadfile、loadstring、dofile、require的区别

  1. loadfile:传入路径,编译代码成中间码并且返回编译后的 chunk 作为一个函数,而不执行代码;
  2. loadstring(lua5.4中已废弃,改为load):跟loadfile类似,但是传入的是string,可以直接执行lua代码,注意返回的也是一个函数,需要手动调用;
  3. dofile:传入lua文件的路径,执行loadfile后调用一次,代码如下:
function dofile (filename)
  local f = assert(loadfile(filename)) --如果 loadfile 失败 assert 会抛出错误。
  return f() -- 根据loadfile的返回函数运行一遍
end
  1. require:跟dofile类型,不同的是require还具有搜索目录加载文件的功能,以及会判断文件是否已经加载过,若加载过则不再加载,代码如下:
function require(name)
    if not package.loaded[name] then --模块是否已加载?
        local loader = findloader(name)
        if loader == nil then
            error("unable to load module"..name)
        end
        package.loaded[name] = true --将模块标记为已加载
        local res = loader(name)    --初始化模块
        if res ~=nil then
            package.loaded[name] = res
        end 
    end     
    return package.loaded[name]
end

如果希望加载过的文件再次加载,则执行

package.loaded[name] = nil

然后再次require即可

3. 如何在Lua中同步调用异步函数

思路:使用Lua的协程,异步调用时先挂起,执行结束后恢复即可,代码如下(参考链接

--传入一个async方法
local function async_to_sync(async_func, callback_pos)
    --返回一个sync方法
    return function(...)
        --取当前正在运行的协程,若不存在则报错(说明只能在协程中使用!)
        local _co = coroutine.running() or error ('this function must be run in coroutine')
        --定义 结果
        local rets
        --定义 等待标记
        local waiting = false
        --定义 完成时的回调
        local function cb_func(...)
            if waiting then
                --等待后回调的情况
                --从挂起的位置恢复协程,并传递结果
                assert(coroutine.resume(_co, ...))
            else
                --未等待就直接回调的情况,直接设置结果
                rets = {
    
    ...}
            end
        end
         
        --参数列表 把 “回调” 加入参数列表(默认加入最后)
        local params = {
    
    ...}
        table.insert(params, callback_pos or (#params + 1), cb_func)
 
        --调用异步方法,把 “通过同步方法传入的参数” 和 “回调” 全部传给异步方法。
        async_func(unpack(params))
 
        --如果没有立即返回,则标记为等待并挂起
        if rets == nil then
            waiting = true
            --挂起! "coroutine.yield()" 的返回值为 coroutine.resume 传入的第二个参数
            rets = {
    
    coroutine.yield()}
        end
        
        --将结果同步返回
        return unpack(rets)
    end
end

4. 什么情况下调用函数可以省略 “()”

在Lua中,当函数的入参只有一个表或者字符串时,可以省略“()”进行函数调用,以下代码都是合法的:

print "hello world"
print {
    
    }

注意,在这种情况下如果函数的参数列表是不定参数,则 “…” 等价于一个传入的参数本身,以下代码都是合法的:

function test_func(...)
    print("test:",...)
    test_func2(...,10)
end
function test_func2(tb,num)
    print("test1:",tb,num)
end
test_func {
    
    }

输出:

test:	table: 000001C390C91E40
test1:	table: 000001C390C91E40	10

5. 如何实现Lua的深拷贝

思路:对于非table类型的数据,无需拷贝直接返回自身,否则递归遍历拷贝table中的所有元素,注意需要用元表来“拷贝”目标的元表的数据,代码如下:

-- Lua table deep copy
function clone(object)
    local lookup_table = {
    
    }
    local function _copy(object)
        if type(object) ~= "table" then		--非table直接返回
            return object
        elseif lookup_table[object] then	--找过的就不用再找了
            return lookup_table[object]
        end
        local new_table = {
    
    }
        lookup_table[object] = new_table
        for key, value in pairs(object) do
            new_table[_copy(key)] = _copy(value)	--递归遍历table的元素
        end
        return setmetatable(new_table, getmetatable(object))	--拷贝目标的元表的数据
    end
    return _copy(object)
end

6. Lua与C之间的相互调用

Lua作为一种轻量型的“胶水”语言,很多情况下需要和其他高级语言进行相互调用以满足日常需求。
Lua完全不同于其他高级语言,要实现相互调用,需要借助一个“虚拟栈”的东西,再按照约定好的操作流程(相当于协议),就可以进行调用。
参考资料:
1. Lua与C语言的互相调用
2. tolua之wrap文件的原理与使用

7.如何让一个表变为只读(ReadOnly)

利用元方法__newindex可以干预写入行为的特性即可:

function table.SetReadOnly(tb)  
    setmetatable(tb, {
    
    
        __newindex = function(t, k, v)
            error("attempt to modify a read-only table!")
        end
    })
end

8.如何用Lua实现一个类(Class)

思路:首先类最重要的特性是继承,也就是访问一个子类的函数或字段时,若子类没有该函数或字段,则向上递归尝试寻找父类的同名函数与字段(即子类隐式包含父类的函数与字段);Lua没有继承语法糖,但是可以通过元表和元方法模拟类的行为。

---创建类
---@param className string 类名
---@param superClass table|function|nil Class 父类
function Class(className, superClass)
    local clazz = {
    
    }
    clazz._className = className
    local superType = type(superClass)

    if superType == "function" or superType == "table" then
        setmetatable(clazz,{
    
    __index = superClass})		--实现继承的关键代码
        clazz._super = superClass
    else
        clazz.Ctor = function ()  end 		--提供一个默认的构造函数
    end

    function clazz.New(...)
        local instance = {
    
    }
        setmetatable(instance,{
    
    __index = clazz})		--实现:类的实例访问类的字段或函数
        instance._class = clazz
        instance:Ctor(...)
        return instance
    end

    return clazz
end

避坑指南:派生类需要调用父类的同名函数时,不能使用 self._super:XXX() 的方式,而要用 className._super.XXX(self) 的方式,比如:

local Object = Class("Object")
function Object:Ctor()
	self.tb = {
    
    }
end
local ClsA = Class("ObjectA",Object)
function ClsA:Ctor()
    -- self._super:Ctor() -- 错误
    ClsA._super.Ctor(self) -- 正确
end

原因在于,派生类的 _super 指向的是一个父类表,不同的对象调用 self._super:Ctor() 之后, self.tb 这张表实际上是存储在父类表中,而不是存储于实例对象中,因此不同的派生对象通过 self.tb 访问时,实际上都是访问父类中的 tb。而 ClsA._super.Ctor(self) 因为是用点访问而不是冒号访问,入参传入的是自身self,因而 self.tb = {} 就等同于 ClsA.tb = {} 自然也就解决了这个问题。

猜你喜欢

转载自blog.csdn.net/weixin_37658157/article/details/123604068
今日推荐