在lua中,可以通过元表来实现类、对象、继承等。与元表相关的方法有setMetatable()、__index、getMetatable()、__newindex。
关键:实现Lua面向对象可以分解为类的定义和类的实例化两个问题。类的定义主要是实现继承,即怎么让子类拥有父类的方法集。类的实例化需要解决实例如何共享类的方法集,但独享自己的成员变量实例。
方案:子类在定义时复制所有基类的方法,在实例化时将该类作为Metatable的__index赋值给实例。这就是cocos2dx里面的lua class的实现。
function clone(object)--clone函数
local lookup_table = {}--新建table用于记录
local function _copy(object)--_copy(object)函数用于实现复制
if type(object) ~= "table" then
return object ---如果内容不是table 直接返回object(例如如果是数字\字符串直接返回该数字\该字符串)
elseif lookup_table[object] then
return lookup_table[object]--这里是用于递归滴时候的,如果这个table已经复制过了,就直接返回
end
local new_table = {}
lookup_table[object] = new_table--新建new_table记录需要复制的二级子表,并放到lookup_table[object]中.
for key,value in pairs(object) do
new_table[_copy(key)] = _copy(value)--遍历object和递归_copy(value)把每一个表中的数据都复制出来
end
return setMetatable(new_table,getMetatable(object))--每一次完成遍历后,就对指定table设置Metatable键值
end
return _copy(object)--返回clone出来的object表指针/地址
end
--[[
clone 深度克隆一个值。
格式:value = clone(值)
用法示例:
-- 下面的代码,t2 是 t1 的引用,修改 t2 的属性时,t1 的内容也会发生变化
local t1 = {a = 1,b = 2}
local t2 = t1
t2.b = 3 -- t1 = {a = 1,b = 3} <-- t1.b 发生变化
-- clone() 返回 t1 的副本,修改 t2 不会影响 t1
local t1 = {a = 1,b = 2}
local t2 = clone(t1)
t2.b = 3 -- t1 = {a = 1,b = 2} <-- t1.b 不受影响
--]]
--Create an class.
function class(classname,super)--super为继承的类
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 = {}
--如果父类是表则复制成员并且设置这个类的继承信息
--如果是函数类型则设置构造方法并且设置ctor函数
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
通过该方法,我们可以很方便的定义一个class、继承一个class。
--声明一个类:
MyClass = class("MyClass")
function MyClass:ctor()
print("MyClass:ctor()")
end
--定义一个对象
local myclass = MyClass:new()
--继承一个类:
--继承一个函数
GameLayer = class("GameLayer",function()local layer = cc.Layer:create() return layer end)
local gamelayer = GameLayer:new()
ctor=constructor(构造函数)
dtor=destructor(析构函数)
1.在子类构造函数ctor()中要调用父类构造函数ctor(),这用self.super:ctor(param),这句话反应出并不会像C++那样,在创建子类实例时,自动调用父类的构造函数。
2.使用class(classname,super)来子类继承父类只会继承父类的成员函数,而不会继承父类的成员变量。
若要子类既要继承父类的成员函数也要继承父类的成员变量,且在创建子类实例时,自动调用父类的构造函数。这用下面的class(classname,super)
function class(classname,super)
local cls = {}
if super then
cls = {}
for k,v in pairs(super) do cls[k] = v end
cls.super = super
else
cls = {ctor = function() end}
end
cls.__cname = classname
cls.__index = cls
function cls.new(...)
local instance = setMetatable({},cls)
local create
create = function(c,...)
if c.super then -- 递归向上调用create
create(c.super,...)
end
if c.ctor then
c.ctor(instance,...)
end
end
--create(instance,...)--若替换成instance:ctor(...)则先调用自己的构造函数,若自己没有,则调用父类的构造函数
instance:ctor(...)
instance.class = cls
return instance
end
return cls
end
local BaseClass = class("BaseClass",nil)
function BaseClass:ctor(param)
print("baseclass ctor")
self._param = param
self._children = {}
end
function BaseClass:addChild(obj)
table.insert(self._children,obj)
for k,v in ipairs(self._children) do
print(k,v)
end
end
local DerivedClass = class("DerivedClass",BaseClass)
function DerivedClass:ctor(param)
print("derivedclass ctor")
end
local instance = DerivedClass.new("param1")
instance:addChild("child1")
--打印输出
--baseclass ctor
--derivedclass ctor
--1 child1极具有参考价值的文章:
Lua 面向对象实现
扩展阅读:Lua的面向对象程序设计