前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Lua学习笔记:实现一个Lua Class生成器

Lua学习笔记:实现一个Lua Class生成器

原创
作者头像
晨星成焰
发布2024-09-12 10:05:26
680
发布2024-09-12 10:05:26
举报
文章被收录于专栏:Lua学习笔记

前文须知

Lua 中的每个值都可以有一个 元表。 这个 元表 就是一个普通的 Lua 表, 它用于定义原始值在特定操作下的行为。 如果你想改变一个值在特定操作下的行为,你可以在它的元表中设置对应域。 例如,当你对非数字值做加操作时, Lua 会检查该值的元表中的 "__add" 域下的函数。 如果能找到,Lua 则调用这个函数来完成加这个操作。

表的简单使用可参考 :Lua学习笔记:Lua里table表的使用例及介绍

元表的简单使用可参考:Lua学习笔记:Lua里metatable元表的使用

而熟悉Lua表和元表的都知道,通过元表的 __index 字段可以让表 t 获得一些本身没有的字段, 通过这样的一个形式,我们就可以达到从实例中调用类的方法,这样我们就可以把Lua的元表比作C++中的纯虚类,通过把Lua的元表当做一个普通表的 方法类,去实现Lua Class.😈

1.从Lua的层面去进行Lua Class实现

代码语言:cpp
复制
#include <iostream>
#include <lua.hpp>

int main()
{
	// 创建一个虚拟机
	lua_State* L = luaL_newstate();

	// 加载一些常用的系统库
	luaL_openlibs(L);

	// 加载lua文件并执行
	if (luaL_dofile(L, "LuaClass.lua"))
	{
		// 在lua中 -1表示栈顶 如果出错 出错结果会放置在栈顶中
		printf("%s\n", lua_tostring(L, -1));
	}

	// 关闭虚拟机
	lua_close(L);
	return 0;

}

1.类的创造和实例化使用

面向对象三大特性包括:封装、继承、多态。

通过元表的 __index 字段可以让表 t 获得一些本身没有的字段, 通过这样的一个形式,我们就可以达到从实例中调用类的方法,但是实例的成员变量又是相互独立的。另外,__index 也可以是方法。

代码语言:lua
复制
-- LuaClassP.lua

-- 定义一个Lua Class生成器
local Class = function(className)
    local class = {_className = className}

    class.ctor = false -- 构造函数声明
    
    -- ... 是可变参数
    class.new = function(...)
        local tab = {}
        setmetatable(tab, {__index = class})

        tab:ctor(...)
        return tab
    end
    return class
end

-- 定义一个类 可以看做C++中的类继承纯虚类
local C1 = Class("C1")

-- 构造函数ctor具体实现
function C1:ctor(x, y, z)
    -- 记录成员数据
    self.x = x
    self.y = y
    self.z = z

end


-- 定义一个方法 可以看做C1类自己的方法
function C1:Print() 
    -- 遍历成员变量
    local values = {}
    for k, v in pairs(self) do
        if type(v) ~= 'function' then
            table.insert(values, tostring(v))
        end
    end
    print(table.concat(values, ' '))
end

-- new一个C1对象实例 可以看做C++中的new
local c1 = C1.new(1, 2, 3)
c1:Print() -- 输出 1  2  3

print(c1._className) -- 输出C1

2.类的继承和多态

类的继承通过自定义一个super参数配合元表的__index实现

如果提供了 super 参数,则设置类的元表为父类,以便在当前类中找不到方法或属性时可以去__index域查找父类。

代码语言:lua
复制
-- LuaClassP.lua

-- 定义一个类生成器函数
local Class = function(className, super)
    local class = {_className = className, super = super}

    if super then
        -- 设置类的元表,此类中没有的,可以查找父类是否含有
        setmetatable(class, {__index = super})
    end

    class.ctor = false -- 构造函数声明
    
    -- ... 是可变参数
    class.new = function(...)
        local tab = {}
        setmetatable(tab, {__index = class})
        class:ctor(...)
        return tab
    end
    return class
end


-- 定义一个类 可以看做C++中的类继承抽象类接口
local BaseClass  = Class("BaseClass")

-- 基类的构造函数
function BaseClass:ctor(x, y)
    self.x = x
    self.y = y
end


-- 基类的方法
function BaseClass:PrintBase()
    print("BaseClass: x =", self.x, "y =", self.y)
end

-- 创建一个 BaseClass 对象实例
local baseObj = BaseClass.new(1, 2)
baseObj:PrintBase() -- 输出 BaseClass: x = 1 y = 2

-- 打印类名
print(baseObj._className) -- 输出 BaseClass


-- 定义一个派生类 
local DerivedClass = Class("DerivedClass", BaseClass)

-- 派生类的构造函数
function DerivedClass:ctor(x, y, z)
    -- 调用基类的构造函数
    self.super:ctor(x, y)
    self.z = z
end

-- 派生类重载基类方法
function DerivedClass:PrintBase()
    print("DerivedClass: x =", self.x, "y =", self.y, "z =", self.z)
end

-- 派生类的方法
function DerivedClass:PrintDerived()
    print("DerivedClass: z =", self.z)
end

-- 创建一个 DerivedClass 对象实例
local derivedObj = DerivedClass.new(1, 2, 3)
derivedObj:PrintBase() -- 输出 DerivedClass: x = 1 y = 2 z = 3
derivedObj:PrintDerived() -- 输出 DerivedClass: z = 3

print(derivedObj._className) -- 输出 DerivedClass

2.从C/C++的交互层面进行Lua Class实现

从C/C++层面去实现也是要借助元表的形式,如果使用了依附于 Lua 绑定库(如 sol2、tolua++、luabind 等),这些库提供了 C++ 代码与 Lua 脚本交互的能力,实现起来会更加方便简洁。

以下提供一个无库函数使用的简单Lua Class实现使用例。

Tips:一般最好自己实现一个__gc字符段对应的析构函数,这样方便释放在C/C++创造的一些内存使用,毕竟Lua只会释放自己的内存使用,C/C++层面的内存使用需要自己释放。

Lua垃圾自动回收机制文章可参考:

Lua GC 的工作原理-云风

代码语言:cpp
复制
#include <stdio.h>
#include <lua.hpp>
#define LUA_TEST "LUA_TEST"

//元表成员变量声明
struct tagTest
{
	int a;
};

//成员方法类比于析构函数,lua是垃圾自动回收机制,当对象不再使用时会自动删除
static int f_gc(lua_State* L)
{
	struct tagTest* p = (struct tagTest*)luaL_checkudata(L, 1, LUA_TEST);// 测试是否属于该元表 失败返回NULL
	printf("调用gc销毁了对象");
	return 0;
}

static int f_tostring(lua_State* L)
{
	struct tagTest* p = (struct tagTest*)luaL_checkudata(L, 1, LUA_TEST);// 测试是否属于该元表 失败返回NULL
	lua_pushstring(L, "f_tostring LUA_TEST");
	return 1;// 返回值表示压入的几个参数
}

static int f_GetA(lua_State* L)
{
	struct tagTest* p = (struct tagTest*)luaL_checkudata(L, 1, LUA_TEST);// 测试是否属于该元表 失败返回NULL
	lua_pushinteger(L, p->a);
	return 1;// 返回值表示压入的几个参数
}

static int f_SetA(lua_State* L)
{
	printf("%d\n", lua_gettop(L));
	struct tagTest* p = (struct tagTest*)luaL_checkudata(L, 1, LUA_TEST);// 测试是否属于该元表 失败返回NULL
	int a = luaL_checkinteger(L, 2);
	p->a = a;
	return 0;// 返回值表示压入的几个参数
}

/*
** 元表处理方法
*/
static const luaL_Reg flib[] = {
	{"SetA",f_SetA},
	{"GetA",f_GetA},
	{"__gc", f_gc},//删除时调用
	{"__tostring", f_tostring},
	{NULL, NULL}
};


static void createmeta(lua_State* L) {

	luaL_newmetatable(L, LUA_TEST);  /* 创建一个元表*/

	lua_pushvalue(L, -1);  /* 复制指定位置的元素至栈顶 */
	lua_setfield(L, -2, "__index");  /* metatable.__index = metatable 会弹出栈顶元素 */

	luaL_setfuncs(L, flib, 0);  /* 将方法添加进入元表 */

	lua_pop(L, 1);  /* 弹出newmetabhle的元表 */
}

static int createTest(lua_State* L)
{
	//创建一个用户数据 lua内部会自动释放
	struct tagTest* p = (struct tagTest*)lua_newuserdata(L, sizeof(struct tagTest));
	luaL_setmetatable(L, LUA_TEST);// 和元表进行关联
	return 1;
}

int LuaTest8()
{
	// 创建一个虚拟机
	lua_State* L = luaL_newstate();

	// 加载一些常用的系统库
	luaL_openlibs(L);

	// 创建一个元表
	createmeta(L);

	lua_pushcfunction(L, createTest);
	lua_setglobal(L, "createTest");


	// 加载lua文件并执行
	if (luaL_dofile(L, "Test8.lua"))
	{
		// 在lua中 -1表示栈顶 如果出错 出错结果会放置在栈顶中
		printf("%s\n", lua_tostring(L, -1));
	}

	// 关闭虚拟机
	lua_close(L);
	return 0;
}
代码语言:lua
复制
-- Test8.lua
local test = createTest()

test:SetA(10)
print(test:GetA())
print(test)

附加

禁止Lua创造全局变量

代码语言:lua
复制
-- 通过设置全局表的元表 禁止创造全局变量
setmetatable(_G, {
    __newindex = function(t, key, value)
        print("Cannot create global variable '" .. key .. "'")
    end
})

最近在试着找工作/(ㄒoㄒ)/~~,本篇文章纰漏难以细究,若有差错,还望指出,笔者感激不尽。

参考文章

云凤-在 Lua 中实现面向对象

云凤博客-在 Lua 中实现面向对象

Lua 面向对象(实现类的创建和实例化、封装、继承、多态)

Lua class 的几种实现

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

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

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

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

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前文须知
  • 1.从Lua的层面去进行Lua Class实现
    • 1.类的创造和实例化使用
      • 2.类的继承和多态
      • 2.从C/C++的交互层面进行Lua Class实现
      • 附加
      • 参考文章
      领券
      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档