zoukankan      html  css  js  c++  java
  • 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。

  • 相关阅读:
    【秒懂音视频开发】10_PCM转WAV
    【秒懂音视频开发】09_播放PCM
    【秒懂音视频开发】08_音频录制02_编程
    【秒懂音视频开发】07_音频录制01_命令行
    【秒懂音视频开发】06_Qt开发基础
    高考数学考点关联表[Ⅳ]
    高考数学考点关联表[Ⅲ]
    高考数学考点关联表[Ⅱ]
    高考数学考点关联表[Ⅰ]
    反比例函数
  • 原文地址:https://www.cnblogs.com/yaukey/p/4568202.html
Copyright © 2011-2022 走看看