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。

  • 相关阅读:
    GitLab 介绍
    git 标签
    git 分支
    git 仓库 撤销提交 git reset and 查看本地历史操作 git reflog
    git 仓库 回退功能 git checkout
    python 并发编程 多进程 练习题
    git 命令 查看历史提交 git log
    git 命令 git diff 查看 Git 区域文件的具体改动
    POJ 2608
    POJ 2610
  • 原文地址:https://www.cnblogs.com/yaukey/p/4568202.html
Copyright © 2011-2022 走看看