上一篇文章给了一个面向对象的方案,美中不足的是没有析构函数 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) * + tonumber(minorv) * + (((string.len(rev) == ) and ) or tonumber(rev)) -- Declare current version number.
TX_VERSION = VersionNumber
TX_VERSION_510 =
TX_VERSION_520 =
TX_VERSION_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。

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

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

    Cocos2d-x 脚本语言Lua中的面向对象 面向对象不是针对某一门语言,而是一种思想.在面向过程的语言也能够使用面向对象的思想来进行编程. 在Lua中,并没有面向对象的概念存在,没有类的定义和子类 ...

  2. lua中的面向对象编程

    简单说说Lua中的面向对象 Lua中的table就是一种对象,看以下一段简单的代码: 上述代码会输出tb1 ~= tb2.说明两个具有相同值得对象是两个不同的对象,同时在Lua中table是引用类型的 ...

  3. Lua和C++交互 学习记录之九:在Lua中以面向对象的方式使用C++注册的类

    主要内容转载自:子龙山人博客(强烈建议去子龙山人博客完全学习一遍) 部分内容查阅自:<Lua 5.3  参考手册>中文版 译者 云风 制作 Kavcc vs2013+lua-5.3.3 在 ...

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

    [游戏开发]在Lua中实现面向对象特性——模拟类.继承.多态   阅读目录 一.简介 二.前提知识 三.Lua中实现类.继承.多态 四.总结 回到顶部 一.简介 Lua是一门非常强大.非常灵活的脚本语 ...

  5. Lua中的面向对象编程详解

    简单说说Lua中的面向对象 Lua中的table就是一种对象,看以下一段简单的代码: 复制代码代码如下: local tb1 = {a = 1, b = 2}local tb2 = {a = 1, b ...

  6. lua 中的面向对象

    lua 是一种脚步语言,语言本身并不具备面向对象的特性. 但是我们依然可以利用语言的特性,模拟出面向对象的特性. 面向对象的特性通常会具备:封装,继承,多态的特性,如何在lua中实现这些特性,最主要的 ...

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

    一.简介 Lua是一门非常强大.非常灵活的脚本语言,自它从发明以来,无数的游戏使用了Lua作为开发语言.但是作为一款脚本语言,Lua也有着自己的不足,那就是它本身并没有提供面向对象的特性,而游戏开发是 ...

  8. Lua中的userdata

    [话从这里说起] 在我发表<Lua中的类型与值>这篇文章时,就有读者给我留言了,说:你应该好好总结一下Lua中的function和userdata类型.现在是时候总结了.对于functio ...

  9. 9----Lua中的面向对象

    什么是面向对象? 使用对象.类.继承.封装.消息等基本概念来进行程序设计 面向对象最重要的两个概念就是:对象和类 对象是系统中用来描述客观事物的一个实体,它是构成系统的一个基本单位 一个对象由一组属性 ...

随机推荐

  1. 解决UIScrollView 的点击事件

    目前有两种方法 第一种 通过 Category 扩展 UIScrollView 对象,添加触摸事件,(不建议,后续扩展不方便)代码如下 @implementation UIScrollView (Ex ...

  2. svn-主副分支使用

    主改bug 副加功能, :主合并到副(在副中切换主分支),副调试成功,合并回主(在主切换回副分支) 奇葩的实现了需求 主改bug 副加功能, :主合并到副(在副中切换主分支),副调试成功,合并回主(在 ...

  3. http 常用状态码及含义

    http://www.kuaipan.cn/developers/document_status.htm

  4. android alipay(移动支付,异步通知发起失败,但是支付成功)

    问题1:移动支付 demo测试,支付成功,但是异步通知没发起,help notify_url 需要服务器地址,不是本地网址 问题2:这回 支付成功了.也返回到return_url了.但是页面显示验证失 ...

  5. 在ADS上由于volatile惹得祸

    C语言关键字volatile是一个危险的东东,笔者再用ADS做S3C2440定时器中断实验就因为这个关键字出了错.出现错误情况的准确描述是:定义一个变量时没有用volatile关键字,而且紧接着whi ...

  6. asp.net资料! (.NET) (ASP.NET)

    使用SqlBulkCopy类加载其他源数据到SQL表 在数据回发时,维护ASP.NET Tree控件的位置 vagerent的vs2005网站开发技巧 ASP.NET2.0小技巧--内部控件权限的实现 ...

  7. bzoj 3053 HDU 4347 : The Closest M Points kd树

    bzoj 3053 HDU 4347 : The Closest M Points  kd树 题目大意:求k维空间内某点的前k近的点. 就是一般的kd树,根据实测发现,kd树的两种建树方式,即按照方差 ...

  8. SQL server 开启 cmdshell

    GO RECONFIGURE GO GO RECONFIGURE GO EXEC master..xp_cmdshell 'net use Z: \\192.168.11.1\192.168.11.4 ...

  9. UESTC 1425 Another LCIS

    也是一个求最长连续单调区间的问题,不同于HDU 3308LCIS的是,单点更新变成了区间成段增加,没关系同样的方法可破之.由于是成段更新,所以比更新区间小的区间是最大连续区间长度是不变的,所以更新su ...

  10. POJ3984 迷宫问题

    典型BFS. #include <iostream> #include <memory.h> #include <queue> #include <map&g ...