0%

Lua实现Class机制

lua里面实现class机制,比较关键的地方就是class的继承机制,以及class实例化的过程。

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

1
2
3
4
5
6
7
8
9
10
11
12
1. 完全使用setmetatable()的方式实现,每继承一次,都把父类的方法集设置为子类方法集的metatable。这样做应该
最符合class的继承的思想,尽可能的复用逻辑,而且可以做到动态更新父类的方法集后,所有子类都会更新自身方法集。
比如满足某些特殊的动态更新需求。但随着class继承的层次加深,会生成一个复杂的class方法集table(n层的
metatable),毕竟metatable是有额外开销的,所以这个方法不一定是最完美的方案。
2. 将父类方法集copy到子类方法集来实现继承,即每当定义一个新的子类时,都将父类中方法集完整copy到子类方法集
中。这种方法避免了使用metatable()带来的额外开销,但却造成了一些数据冗余(其实并不多),并丧失了父类更新子类
也会自动更新的特性。
3. 方案2的改进版本(也就是云风使用的比较强悍的方式):即同样是采用copy父类方法集的方案,但却改进了copy的机制。
将原本在class定义期执行的方法集copy工作转移到实例的运行期间,采用copy-on-use(等同于copy-on-write的设计
思路)的方式,在子类实例用到父类的某个方法时,才将其copy到子类的方法集中。由于这种copy只发生一次,而且不见
得子类会用到父类方法集中的所有内容(事实如此),所以这个方案相对于方案2来说减少了冗余数据,但却几乎没有增加
任何额外开销。

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

1
2
3
4
5
6
7
8
1. 最烂的方式,方法集copy,即class实例化时,将class的方法集直接copy给实例的数据table。这样的好处就是每个
实例创建后,外界除非直接操作实例的数据table,否则其它行为都不会影响到这个实例的所有属性和特征(也许可以满
足某些特殊的需求吧),同时也省掉了一次metatable的查找开销。缺点很明显,实例化过程低效,而且产生大量的冗余信
息(或者这里也采用copy-on-use的思想)?
2. 采用将class方法集设置为实例的metatable的方式,使实例享有class的方法集(这要求实例的数据类型必须可以拥
有自己的metatable,即只能是table或userdata)。这个方案更优雅一些,而且符合class的共享思想并且实例化开销
很小,应该是实现实例化的最佳方案了。在实现子类的初始化函数时,一般的思路都是先生成一个父类的实例,再强制将
当前子类的方法集设置为这个实例的metatable。

云风的实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
local _class={}
function class(super)
local class_type={}
class_type.ctor=false
class_type.super=super
class_type.new=function(...)
local obj={}
do
local create
create = function(c,...)
if c.super then
create(c.super,...)
end
if c.ctor then
c.ctor(obj,...)
end
end
create(class_type,...)
end
setmetatable(obj,{ __index=_class[class_type] })
return obj
end
local vtbl={}
_class[class_type]=vtbl
setmetatable(class_type,{__newindex=
function(t,k,v)
vtbl[k]=v
end
})
if super then
setmetatable(vtbl,{__index=
function(t,k)
local ret=_class[super][k]
vtbl[k]=ret
return ret
end
})
end
return class_type
end

使用方式:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
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 的词,作为构造函数的名字。下面是继承:

1
2
3
4
5
6
7
8
9
test=class(base_type)	-- 定义一个类 test 继承于 base_type

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

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

测试样例:

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

项目中的实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
function class(classname, super)
--superType获取父类类型,可以使nil、function以及table.
local superType = type(super)
local cls

--如果父类既不是函数也不是table则说明父类为空
if superType ~= "function" and superType ~= "table" then
superType = nil
super = nil
end
--如果父类的类型是函数或者是C对象
if superType == "function" or (super and super.__ctype == 1) then
-- inherited from native C++ Object
cls = {}
--如果父类是表则复制成员并且设置这个类的继承信息
if superType == "table" then
-- copy fields from super
for k,v in pairs(super) do cls[k] = v end
cls.__create = super.__create
cls.super = super
else
cls.__create = super
cls.ctor = function() end
end
--设置类型的名称
cls.__cname = classname
cls.__ctype = 1

--定义该类型的创建实例的函数为基类的构造函数后复制到子类实例
--并且调用子数的ctor方法
function cls.new(...)
local instance = cls.__create(...)
-- copy fields from class to native object
for k,v in pairs(cls) do instance[k] = v end
instance.class = cls
instance:ctor(...)
return instance
end

else
--如果是继承自普通的lua表,则设置一下原型,并且构造实例后也会调用ctor方法
-- inherited from Lua Object
if super then
cls = {}
setmetatable(cls, {__index = super})
cls.super = super
else
cls = {ctor = function() end}
end

cls.__cname = classname
cls.__ctype = 2 -- lua
cls.__index = cls

function cls.new(...)
local instance = setmetatable({}, cls)
instance.class = cls
instance:ctor(...)
return instance
end
end

return cls
end