加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 大数据 > 正文

在 Lua 中实现面向对象

发布时间:2020-12-14 22:26:52 所属栏目:大数据 来源:网络整理
导读:http://blog.codingnow.com/2006/06/oo_lua.html 在 Lua 中实现面向对象 在 pil 中,lua 的作者推荐了一种方案来实现 OO,比较简洁,但是我依然觉得有些繁琐。 这里给出一种更漂亮一点的解决方案:为了贴代码和修改方便,我把它贴在了 wiki 上。 Lua 中实现

http://blog.codingnow.com/2006/06/oo_lua.html


在 Lua 中实现面向对象

在 pil 中,lua 的作者推荐了一种方案来实现 OO,比较简洁,但是我依然觉得有些繁琐。

这里给出一种更漂亮一点的解决方案:为了贴代码和修改方便,我把它贴在了 wiki 上。

Lua 中实现面向对象

在这个方案中,只定义了一个函数 class(super) ,用这个函数,我们就可以方便的在 lua 中定义类:

base_type=class()       -- 定义一个基类 base_type

function base_type:ctor(x)  -- 定义 base_type 的构造函数
    print("base_type ctor")
    self.x=x
end

function base_type:print_x()    -- 定义一个成员函数 base_type:print_x
    print(self.x)
end

function base_type:hello()  -- 定义另一个成员函数 base_type:hello
    print("hello base_type")
end

以上是基本的 class 定义的语法,完全兼容 lua 的编程习惯。我增加了一个叫做 ctor 的词,作为构造函数的名字。

下面看看怎样继承: test=class(basetype) -- 定义一个类 test 继承于 basetype

function test:ctor()    -- 定义 test 的构造函数
    print("test ctor")
end

function test:hello()   -- 重载 base_type:hello 为 test:hello
    print("hello test")
end

现在可以试一下了:

a=test.new(1)   -- 输出两行,base_type ctor 和 test ctor 。这个对象被正确的构造了。
a:print_x() -- 输出 1 ,这个是基类 base_type 中的成员函数。
a:hello()   -- 输出 hello test ,这个函数被重载了。

其实,实现多重继承也并不复杂,这里就不再展开了。更有意义的扩展可能是增加一个 dtor :)

ps. 这里用了点小技巧,将 self 绑定到 closure 上,所以并不使用 a:hello 而是直接用 a.hello 调用成员函数。这个技巧并不非常有用,从效率角度上说,还是不用为好。

所有脚本语言都被人称为scheme的近亲。我还觉得C++的模板也简直就跟Lisp一个样呢

我认为用 lua 表现 OO ,比 C++ 强,不弱于 ruby 。

btw,lua 是个很优美的语言 IMHO 。lua 应该属于 scheme 的近亲。

| (11) July 27,2007 12:35 AM

使用lua就不应该OO。麻烦而且难于理解。 Lua 不是为OO设计的。

: 在 lua 里只是一个语法糖。

函数调用的时候 obj:func(arg) 其实等价于 obj.func(obj,arg)

函数定义时 function obj:func(arg) 等价于 function obj.func(self,arg)

在考虑问题的时候,可以无试 : 的存在,脑子里转换成以上 . 的形式看就可以了。

| (9) November 2,2006 04:03 PM

嗯,谢谢,我也怀疑查找这个东西的时候指向了闭包。
还有一个问题:
我如果在c里面call一个table里面的方法。
如果lua里面的函数有的写作:
function a.b(x,y)
end
有的写作
function a:c(x,y)
end
当写作a.b(x,y)时候我们不需要把self push进去。
当写作a:c(x,y)的时候我们应该把 self push进去。
问题是我在c里面不知道究竟写成了哪一种,问有没有办法在c里面知道究竟是写成了哪一种呢?谢谢!

这里有点问题,出在这里:

  _class[vtbl]=function (t,k)
      return function(...)
         return vtbl[k](t,...)
      end
  end

因为你第一次访问 vtbl 的时候,就生成了一个 closure ,导致以后的对之的访问导向了这个 closure 。

我把 wiki 上的代码改了,直接把 vtbl 设置 到 metatable __index 中去。

不过这样,就必须用 : 而不是 . 来做成员函数调用。(当然这样也符合 lua 的习惯)

| (7) October 30,2006 11:48 PM

有个问题,不大明白是怎么回事:
a = class()
b = a.new()
print(b.c) --输出:function: 0046e970

b.c = nil
print(b.c) --输出:function: 0082340

为什么不是输出nil呢?想不明白!
请教各位前辈!

这是叫 override,不过我喜欢把这个词译为重载

| (5) June 23,2006 01:25 PM

a.hello() -- 输出 hello test ,这个函数被重载了。

---------------------------
这应该不叫overload吧?应该是override!

我个人认为lua在语言层次实现class意义不大,反而失去了lua的部分灵活性。

其实 lua 也是一步步设计出来的,metatable 是在 lua 的第 5 版才加入。而且在 lua 5 诞生了很久,大部分习惯使用 lua 的人还是当成 lua 4 在用。

怎么用好 metatable 还有很多可挖掘的地方。

我觉得这里提供的方案比 pil 上的方案更完善的其实并不是让类的定义和继承更直观,而是提供了一个自然的 ctor 。这个 ctor 比 C++ 更方便就是可以向基类传递构造参数。

实现一个 dtor 也并不难,而且还可以利用一个 weaktable cache 住不要的对象,等再次构造同类对象的时候重用。

| (2) June 13,2006 01:45 PM

我参与的项目里面也实现了lua中的class机制,这里趁机小结一下:p
lua里面实现class机制,比较关键的地方就是class的继承机制,以及class实例化的过程。

class继承机制的关键是怎么让子类拥有父类的方法集:

1.完全使用setmetatable()的方式实现,每继承一次,都把父类的方法集设置为子类方法集的metatable。这样做应该最符合class 的继承的思想,尽可能的复用逻辑,而且可以做到动态更新父类的方法集后,所有子类都会?焙蛳碛姓庀罡?碌奶匦?比如满足某些特殊的动态更新需求)。但随着 class继承的层次加深,会生成一个复杂的class方法集table(n层的metatable),毕竟metatable是有额外开销的,所以这个 方法不一定是最完美的方案。

2.将父类方法集copy到子类方法集来实现继承,即每当定义一个新的子类时,都将父类中方法集完整copy到子类方法集中。这种方法避免了使用metatable()带来的额外开销,但却造成了一些数据冗余(其实并不多),并丧失了父类更新子类也会自动更新的特性。

3.方案2的改进版本(也就是云风这里使用的比较强悍的方式:P pf),即同样是采用copy父类方法集的方案,但却改进了copy的机制。将原本在class定义期执行的方法集copy工作转移到实例的运行期间,采 用copy-on-use(等同于copy-on-write的设计思路)的方式,在子类实例用到父类的某个方法时,才将其copy到子类的方法集中。由 于这种copy只发生一次,而且不见得子类会用到父类方法集中的所有内容(事实如此),所以这个方案相对于方案2来说减少了冗余数据,但却几乎没有增加任 何额外开销。

class实例化关键是实例如何享有class的方法集:

1.最烂的方式,方法集copy,即class实例化时,将class的方法集直接copy给实例的数据table。这样的好处就是每个实例创建后,外界 除非直接操作实例的数据table,否则其它行为都不会影响到这个实例的所有属性和特征(也许可以满足某些特殊的需求吧),同时也省掉了一次 metatable的查找开销。缺点很明显,实例化过程低效,而且产生大量的冗余信息(或者这里也采用copy-on-use的思想? :p)。

2.采用将class方法集设置为实例的metatable的方式,使实例享有class的方法集(这要求实例的数据类型必须可以拥有自己的 metatable,即只能是table或userdata)。这个方案更优雅一些,而且符合class的共享思想并且实例化开销很小,应该是实现实例化 的最佳方案了。在实现子类的初始化函数时,一般的思路都是先生成一个父类的实例,再强制将当前子类的方法集设置为这个实例的metatable。

目前我们采用的class机制就是上述的两个2号方案(考虑更新到云风的3号方案),自己实现了两个接口class_define()、 class_inherit(),其实完成的功能等同于以上代码中的class()。还是很pf lua的作者,估计在设计lua时就考虑到了面向对象的实现问题,很class的多关键机制(如隐式self参数、metatable机制)都已实现,这 为自己实现class机制提供了强有力的支持。加上脚本的弱数据类型特性,只要思路清晰,在C++中可以实现的class机制lua中基本都能实现一个翻 版,也许下个版本的lua可能会显示支持class:)

| (1) June 13,2006 11:04 AM

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读