Lua 中使用面向对象(续)

  上一篇文章给了一个面向对象的方案,美中不足的是没有析构函数 Destructor,那么这一次就给它加上。

  既然是析构,那么就是在对象被销毁之前做该做的事情,lua 5.1 的 userdata 可以给其 metatable 增加一个 __gc 域,指定一个函数,将会在被回收时调用,这个 __gc 只能用于 userdata,普遍的 table 不支持;到了 lua 5.2 以后,官方支持了给予普通 table 的 metatable 增加 __gc 域,可以在回收前被回调;具体细节可以参考对应版本的 manual。

  userdata 一般是 c 里创建的自定义数据结构,但是如果想在 lua 里做这件事情的该如何实现,理论上 lua 是不支持的,但是作者增加了一个隐藏的非公开测试函数 newproxy 用于创建一个空的 userdata,参数可以选择是否带 metatable。用法如下:

newproxy

newproxy (boolean or proxy)

Undocumented feature of Lua.

Arguments: boolean - returned proxy has metatable or userdata - different proxy created with newproxy

Creates a blank userdata with an empty metatable, or with the metatable of another proxy. Note that, in ROBLOX, creating a proxy with another proxy is disabled and will error.

local a = newproxy(true)
local mt = getmetatable(a)
print( mt ~= nil )

local b = newproxy(a)
print( mt == getmetatable(b) )

local c = newproxy(false)
print( getmetatable(c) ~= nil )

print( a.Name )
mt.__index = {Name="Proxy"}
print( a.Name )
print( b.Name )
-- Output:
true
true
false
attempt to index local ‘a‘ (a userdata value)
Proxy
Proxy

  使用它就可以创建一个空的 userdata 并指定 __gc 操作,在你的对象上保持一个唯一的引用到该 userdata,当你的对象被销毁前 userdata 的 __gc 会被调用。

  对于 5.2 及以后版本的 table 的 __gc,需要注意,你必须在第一次为其设置 metatable 时就制定 __gc,才能开启改标记,否则先设置 metatable,而到其后修改 metatable,增加 __gc 域,是不起作用的。

  下面给出改进过的面向对象方案,注意这里做了版本区分,TxClass:

-- Get current version number.
local _, _, majorv, minorv, rev = string.find(_VERSION, "(%d).(%d)[.]?([%d]?)")
local VersionNumber = tonumber(majorv) * 100 + tonumber(minorv) * 10 + (((string.len(rev) == 0) and 0) or tonumber(rev))

-- Declare current version number.
TX_VERSION = VersionNumber
TX_VERSION_510 = 510
TX_VERSION_520 = 520
TX_VERSION_530 = 530

-- The hold all class type.
local __TxClassTypeList = {}

-- The inherit class function.
local function TxClass(TypeName, SuperType)
    -- Create new class type.
    local ClassType = {}

    -- Set class type property.
    ClassType.TypeName = TypeName
    ClassType.Constructor = false
    ClassType.SuperType = SuperType

    -- The new alloc function of this class.
    ClassType.new = function (...)
        -- Create a new object first and set metatable.
        local Obj = {}

        -- Give a tostring method.
        Obj.ToString = function (self)
            local str = tostring(self)
            local _, _, addr = string.find(str, "table%s*:%s*(0?[xX]?%x+)")
            return ClassType.TypeName .. ":" .. addr
        end

        -- Do constructor recursively.
        local CreateObj = function (Class, Object, ...)
            local Create
            Create = function (c, ...)
                if c.SuperType then
                    Create(c.SuperType, ...)
                end

                if c.Constructor then
                    c.Constructor(Object, ...)
                end
            end

            Create(Class, ...)
        end

        -- Do destructor recursively.
        local ReleaseObj = function (Class, Object)
            local Release
            Release = function (c)
                if c.Destructor then
                    c.Destructor(Object)
                end

                if c.SuperType then
                    Release(c.SuperType)
                end
            end

            Release(Class)
        end

        -- Do the destructor by lua version.
        if TX_VERSION < TX_VERSION_520 then
            -- Create a empty userdata with empty metatable.
            -- And mark gc method for destructor.
            local Proxy = newproxy(true)
            getmetatable(Proxy).__gc = function (o)
                ReleaseObj(ClassType, Obj)
            end

            -- Hold the one and only reference to the proxy userdata.
            Obj.__gc = Proxy

            -- Set metatable.
            setmetatable(Obj, {__index = __TxClassTypeList[ClassType]})
        else
            -- Directly set __gc field of the metatable for destructor of this object.
            setmetatable(Obj,
            {
                __index = __TxClassTypeList[ClassType],

                __gc = function (o)
                    ReleaseObj(ClassType, o)
                end
            })
        end

        -- Do constructor for this object.
        CreateObj(ClassType, Obj, ...)
        return Obj
    end

    -- Give a ToString method.
    ClassType.ToString = function (self)
        return self.TypeName
    end

    -- The super class type of this class.
    if SuperType then
        ClassType.super = setmetatable({},
        {
            __index = function (t, k)
                local Func = __TxClassTypeList[SuperType][k]
                if "function" == type(Func) then
                    t[k] = Func
                    return Func
                else
                    error("Accessing super class field are not allowed!")
                end
            end
        })
    end

    -- Virtual table
    local Vtbl = {}
    __TxClassTypeList[ClassType] = Vtbl

    -- Set index and new index of ClassType, and provide a default create method.
    setmetatable(ClassType,
    {
        __index = function (t, k)
            return Vtbl[k]
        end,

        __newindex = function (t, k, v)
            Vtbl[k] = v
        end,

        __call = function (self, ...)
            return ClassType.new(...)
        end
    })

    -- To copy super class things that this class not have.
    if SuperType then
        setmetatable(Vtbl,
        {
            __index = function (t, k)
                local Ret = __TxClassTypeList[SuperType][k]
                Vtbl[k] = Ret
                return Ret
            end
        })
    end

    return ClassType
end

  使用也很简单:

local MyBase = TxClass("MyBase")

function MyBase:Constructor()
    print("MyBase:Constructor")
end

function MyBase:Destructor()
    print("MyBase:Destructor")
end

local MyNew = TxClass("MyNew", MyBase)

function MyNew:Constructor()
    print("MyNew:Constructor")
end

function MyNew:Destructor()
    print("MyNew:Destructor")
end

local cls = MyNew()
cls = nil
collectgarbage()

-- Output:
MyBase:Constructor
MyNew:Constructor
MyNew:Destructor
MyBase:Destructor

  接下来的扩展是,给一个简单的运行时方法:IsA。

时间: 2024-10-10 12:09:09

Lua 中使用面向对象(续)的相关文章

lua中的面向对象编程

简单说说Lua中的面向对象 Lua中的table就是一种对象,看以下一段简单的代码: 上述代码会输出tb1 ~= tb2.说明两个具有相同值得对象是两个不同的对象,同时在Lua中table是引用类型的.我在<Lua中的模块与包>中也总结了,我们是基于table来实现的模块,在table中可以定义函数,也就是说,每个table对象都可以拥有其自己的操作.看一段代码: 上面的代码创建了一个新函数,并将该函数存入Account对象的withDraw字段中,然后我们就可以调用该函数了.不过,在函数中使

Cocos2d-x 脚本语言Lua中的面向对象

Cocos2d-x 脚本语言Lua中的面向对象 面向对象不是针对某一门语言,而是一种思想.在面向过程的语言也能够使用面向对象的思想来进行编程. 在Lua中,并没有面向对象的概念存在,没有类的定义和子类的定义.但相同在Lua中能够利用面向对象的思想来实现面向对象的类继承. 一.复制表的方式面向对象 --Lua中的面向对象 --[[ 复制表方式面向对象 參数为一张表.通过遍历这张表取值,赋给一张空表,最后返回新建的表.来达到克隆表 ]] function clone(tab) local ins =

Lua语言基础汇总(12)-- Lua中的面向对象编程

简单说说Lua中的面向对象 Lua中的table就是一种对象,看以下一段简单的代码: 1 2 3 4 5 6 7 8 9 10 11 12 local tb1 = {a = 1, b = 2} local tb2 = {a = 1, b = 2} local tb3 = tb1   if tb1 == tb2 then      print("tb1 == tb2") else      print("tb1 ~= tb2") end   tb3.a = 3 pri

[转]Lua中的面向对象实现方法

面向对象不是针对某一门语言,而是一种思想,在面向过程的语言也可以使用面向对象的思想来进行编程.在Lua中,并没有面向对象的概念存在,没有类的定义和子类的定义,但同样在Lua中可以利用面向对象的思想来实现面向对象的类继承. 一.复制表的方式面向对象 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45

【游戏开发】在Lua中实现面向对象特性——模拟类、继承、多态

一.简介 Lua是一门非常强大.非常灵活的脚本语言,自它从发明以来,无数的游戏使用了Lua作为开发语言.但是作为一款脚本语言,Lua也有着自己的不足,那就是它本身并没有提供面向对象的特性,而游戏开发是一项庞大复杂的工程,如果没有面向对象功能势必会为开发带来一定的不便.不过幸好Lua中有table这样强大的数据结构,利用它再结合元表(metatable),我们便可以很方便地在Lua中模拟出类.继承和多态等面向对象编程具有的特性. 二.前提知识 按照惯例,我们还是先来熟悉一下必要的前提知识,以便方便

Lua中的userdata

[话从这里说起] 在我发表<Lua中的类型与值>这篇文章时,就有读者给我留言了,说:你应该好好总结一下Lua中的function和userdata类型.现在是时候总结了.对于function,我在<Lua中的函数>这篇文章中进行了总结,而这篇文章将会对Lua中的userdata进行仔细的总结.对于文章,大家如果有任何疑议,都可以在文章的下方给我留言,也可以关注我的新浪微博与我互动.学习,就要分享,我期待你的加入. [userdata是啥?] userdata是啥?简单直译就是用户数

Lua中面向对象编程的理解

模块 模块是一个独立的空间,一个独立的环境,访问模块成员需要先require,并使用"模块名.成员名称"的格式访问.注意:模块是一个table. 类 在lua中所有对象都是一个table,类也是一个table,但类应该是一个只读的table,类的定义是通过创建一个模块实现的. lua代码: module("Student",package.seeall) function study(self) end 在调用module方法时添加package.seeall参数的

在lua中创建字段安全的对象

lua萌新,刚刚学习和使用不到一个月.有不对的地方,还望各路大神不吝赐教. lua中可以用table来模拟对象,但table是可以任意增加键值的.在对象模拟中,暂且也叫它为字段(field)吧.如果在面向对象中,你定义了一个对象,可以在对象以外的地方随意改动这个对象的字段,访问不存在的字段,你想象一下这有多恐怖?比如你定义了一个Vector3{float x = 0; float y = 0; float z = 0;}  我在外面某处加一个float t = 1; 当你在创建并引用这对象的时候

Lua程序转载 - 面向对象的实现探讨

> 转载出处:http://blog.csdn.net/xenyinzen/article/details/3536708 元表概念     Lua中,面向对向是用元表这种机制来实现的.首先,一般来说,一个表和它的元表是不同的个体(不属于同一个表),在创建新的table时,不会自动创建元表.但是,任何表都可以有元表(这种能力是存在的). e.g.t = {}print(getmetatable(t))   --> nilt1 = {}setmetatable(t, t1)assert(getm