http://blog.codingnow.com/2006/06/oo_lua.html
在 Lua 中实现面向对象
在 pil 中,lua 的作者推荐了一种方案来实现 OO,比较简洁,但是我依然觉得有些繁琐。
这里给出一种更漂亮一点的解决方案:为了贴代码和修改方便,我把它贴在了 wiki 上。
在这个方案中,只定义了一个函数 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 调用成员函数。这个技巧并不非常有用,从效率角度上说,还是不用为好。
我认为用 lua 表现 OO ,比 C++ 强,不弱于 ruby 。
btw,lua 是个很优美的语言 IMHO 。lua 应该属于 scheme 的近亲。
使用lua就不应该OO。麻烦而且难于理解。 Lua 不是为OO设计的。
: 在 lua 里只是一个语法糖。
函数调用的时候 obj:func(arg) 其实等价于 obj.func(obj,arg)
函数定义时 function obj:func(arg) 等价于 function obj.func(self,arg)
在考虑问题的时候,可以无试 : 的存在,脑子里转换成以上 . 的形式看就可以了。
嗯,谢谢,我也怀疑查找这个东西的时候指向了闭包。
还有一个问题:
我如果在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 的习惯)
有个问题,不大明白是怎么回事:
a = class()
b = a.new()
print(b.c) --输出:function: 0046e970
b.c = nil
print(b.c) --输出:function: 0082340
为什么不是输出nil呢?想不明白!
请教各位前辈!
这是叫 override,不过我喜欢把这个词译为重载
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 住不要的对象,等再次构造同类对象的时候重用。
我参与的项目里面也实现了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:)
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。
所有脚本语言都被人称为scheme的近亲。我还觉得C++的模板也简直就跟Lisp一个样呢