Lua继承原理(如何实现面向对象)

1.lua中的类

lua中其实是没有类的,有的只是表(table),而类之间的继承也就是将父类的表连到了一起,派生类中没有找到的属性和方法就通过元表查找父类

2.lua中类的属性

classA = {width =10, height=10}

classA={}

classA.width=10

classA.height=10

两种方法都可以,通过点self.width统一调用

3.类方法
function Box:collsion()  
    -- 默认第一个参数隐藏传递self,可以通过self.xxx 调用属性和方法  
end  
  
function Box.create(self)  
    --必须手动传递参数self,否则无法用self.xxx调用属性和方法  
end

函数的声明和调用可以用":"和".",属性调用全部用点"."

4.类与元表的用法

lua查找一个表元素时的规则,其实就是如下3个步骤:

1.在表中查找,如果找到,返回该元素,找不到则继续

2.判断该表是否有元表,如果没有元表,返回nil,有元表则继续

3.判断元表有没有__index方法,如果__index方法为nil,则返回nil;如果__index方法是一个表,则重复1、2、3;如果__index方法是一个函数,则返回该函数的返回值

例如:

father = {  
    house=1  
}  
son = {  
    car=1  
}  
setmetatable(son, father) --把son的metatable设置为father  
print(son.house)  

输出结果是nil,如果代码改为

father = {  
    house=1  
}  
father.__index = father -- 把father的__index方法指向自己  
son = {  
    car=1  
}  
setmetatable(son, father)  
print(son.house)  

输出的结果就为1了

这就解释了为什么我们经常在cocos2dx的类中经常见到如下

local Box = class("Box", function(filename)  
        return cc.Sprite:create(filename)  
    end)  
  
Box.__index = Box 

设置Box的元表的__index方法为自己,当派生类"SmallBox"派生自"Box",如果在SmallBox中查找不到的属性和方法,就检索元表,当然不是直接从元表中直接检索,是检索元表下的__index,如果__index为nil,则返回nil,如果__index是一个表,那么就到__index方法所指的表中查找对应的属性和方法
具体可以参考:Lua查找表元素过程(元表、__index方法是如何工作的)

5.Cocos2dx中的类

lua没有面向对象一说,cocos为我们准备了class的lua端函数,我们参考quick的class函数,里面还有对应的例子

--[[--  
  
创建一个类  
  
~~~ lua  
  
-- 定义名为 Shape 的基础类  
local Shape = class("Shape")  
  
-- ctor() 是类的构造函数,在调用 Shape.new() 创建 Shape 对象实例时会自动执行  
function Shape:ctor(shapeName)  
    self.shapeName = shapeName  
    printf("Shape:ctor(%s)", self.shapeName)  
end  
  
-- 为 Shape 定义个名为 draw() 的方法  
function Shape:draw()  
    printf("draw %s", self.shapeName)  
end  
  
--  
  
-- Circle 是 Shape 的继承类  
local Circle = class("Circle", Shape)  
  
function Circle:ctor()  
    -- 如果继承类覆盖了 ctor() 构造函数,那么必须手动调用父类构造函数  
    -- 类名.super 可以访问指定类的父类  
    Circle.super.ctor(self, "circle")  
    self.radius = 100  
end  
  
function Circle:setRadius(radius)  
    self.radius = radius  
end  
  
-- 覆盖父类的同名方法  
function Circle:draw()  
    printf("draw %s, raidus = %0.2f", self.shapeName, self.raidus)  
end  
  
--  
  
local Rectangle = class("Rectangle", Shape)  
  
function Rectangle:ctor()  
    Rectangle.super.ctor(self, "rectangle")  
end  
  
--  
  
local circle = Circle.new()             -- 输出: Shape:ctor(circle)  
circle:setRaidus(200)  
circle:draw()                           -- 输出: draw circle, radius = 200.00  
  
local rectangle = Rectangle.new()       -- 输出: Shape:ctor(rectangle)  
rectangle:draw()                        -- 输出: draw rectangle  
  
~~~  
  
### 高级用法   
  
class() 除了定义纯 Lua 类之外,还可以从 C++ 对象继承类。  
  
比如需要创建一个工具栏,并在添加按钮时自动排列已有的按钮,那么我们可以使用如下的代码:  
  
~~~ lua  
  
-- 从 cc.Node 对象派生 Toolbar 类,该类具有 cc.Node 的所有属性和行为  
local Toolbar = class("Toolbar", function()  
    return display.newNode() -- 返回一个 cc.Node 对象  
end)  
  
-- 构造函数  
function Toolbar:ctor()  
    self.buttons = {} -- 用一个 table 来记录所有的按钮  
end  
  
-- 添加一个按钮,并且自动设置按钮位置  
function Toolbar:addButton(button)  
    -- 将按钮对象加入 table  
    self.buttons[#self.buttons + 1] = button  
  
    -- 添加按钮对象到 cc.Node 中,以便显示该按钮  
    -- 因为 Toolbar 是从 cc.Node 继承的,所以可以使用 addChild() 方法  
    self:addChild(button)  
  
    -- 按照按钮数量,调整所有按钮的位置  
    local x = 0  
    for _, button in ipairs(self.buttons) do  
        button:setPosition(x, 0)  
        -- 依次排列按钮,每个按钮之间间隔 10 点  
        x = x + button:getContentSize().width + 10  
    end  
end  
  
~~~  
  
class() 的这种用法让我们可以在 C++ 对象基础上任意扩展行为。  
  
既然是继承,自然就可以覆盖 C++ 对象的方法:  
  
~~~ lua  
  
function Toolbar:setPosition(x, y)  
    -- 由于在 Toolbar 继承类中覆盖了 cc.Node 对象的 setPosition() 方法  
    -- 所以我们要用以下形式才能调用到 cc.Node 原本的 setPosition() 方法  
    getmetatable(self).setPosition(self, x, y)  
  
    printf("x = %0.2f, y = %0.2f", x, y)  
end  
  
~~~  
  
**注意:** Lua 继承类覆盖的方法并不能从 C++ 调用到。也就是说通过 C++ 代码调用这个 cc.Node 对象的 setPosition() 方法时,并不会执行我们在 Lua 中定义的 Toolbar:setPosition() 方法。  
  
@param string classname 类名  
@param [mixed super] 父类或者创建对象实例的函数  
  
@return table  
  
]]  
function class(classname, super)  
    local superType = type(super)  
    local cls  
  
    if superType ~= "function" and superType ~= "table" then  
        superType = nil  
        super = nil  
    end  
  
    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  
  
        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  
        -- 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
--[[--  
  
创建一个类  
  
~~~ lua  
  
-- 定义名为 Shape 的基础类  
local Shape = class("Shape")  
  
-- ctor() 是类的构造函数,在调用 Shape.new() 创建 Shape 对象实例时会自动执行  
function Shape:ctor(shapeName)  
    self.shapeName = shapeName  
    printf("Shape:ctor(%s)", self.shapeName)  
end  
  
-- 为 Shape 定义个名为 draw() 的方法  
function Shape:draw()  
    printf("draw %s", self.shapeName)  
end  
  
--  
  
-- Circle 是 Shape 的继承类  
local Circle = class("Circle", Shape)  
  
function Circle:ctor()  
    -- 如果继承类覆盖了 ctor() 构造函数,那么必须手动调用父类构造函数  
    -- 类名.super 可以访问指定类的父类  
    Circle.super.ctor(self, "circle")  
    self.radius = 100  
end  
  
function Circle:setRadius(radius)  
    self.radius = radius  
end  
  
-- 覆盖父类的同名方法  
function Circle:draw()  
    printf("draw %s, raidus = %0.2f", self.shapeName, self.raidus)  
end  
  
--  
  
local Rectangle = class("Rectangle", Shape)  
  
function Rectangle:ctor()  
    Rectangle.super.ctor(self, "rectangle")  
end  
  
--  
  
local circle = Circle.new()             -- 输出: Shape:ctor(circle)  
circle:setRaidus(200)  
circle:draw()                           -- 输出: draw circle, radius = 200.00  
  
local rectangle = Rectangle.new()       -- 输出: Shape:ctor(rectangle)  
rectangle:draw()                        -- 输出: draw rectangle  
  
~~~  
  
### 高级用法  
  
class() 除了定义纯 Lua 类之外,还可以从 C++ 对象继承类。  
  
比如需要创建一个工具栏,并在添加按钮时自动排列已有的按钮,那么我们可以使用如下的代码:  
  
~~~ lua  
  
-- 从 cc.Node 对象派生 Toolbar 类,该类具有 cc.Node 的所有属性和行为  
local Toolbar = class("Toolbar", function()  
    return display.newNode() -- 返回一个 cc.Node 对象  
end)  
  
-- 构造函数  
function Toolbar:ctor()  
    self.buttons = {} -- 用一个 table 来记录所有的按钮  
end  
  
-- 添加一个按钮,并且自动设置按钮位置  
function Toolbar:addButton(button)  
    -- 将按钮对象加入 table  
    self.buttons[#self.buttons + 1] = button  
  
    -- 添加按钮对象到 cc.Node 中,以便显示该按钮  
    -- 因为 Toolbar 是从 cc.Node 继承的,所以可以使用 addChild() 方法  
    self:addChild(button)  
  
    -- 按照按钮数量,调整所有按钮的位置  
    local x = 0  
    for _, button in ipairs(self.buttons) do  
        button:setPosition(x, 0)  
        -- 依次排列按钮,每个按钮之间间隔 10 点  
        x = x + button:getContentSize().width + 10  
    end  
end  
  
~~~  
  
class() 的这种用法让我们可以在 C++ 对象基础上任意扩展行为。  
  
既然是继承,自然就可以覆盖 C++ 对象的方法:  
  
~~~ lua  
  
function Toolbar:setPosition(x, y)  
    -- 由于在 Toolbar 继承类中覆盖了 cc.Node 对象的 setPosition() 方法  
    -- 所以我们要用以下形式才能调用到 cc.Node 原本的 setPosition() 方法  
    getmetatable(self).setPosition(self, x, y)  
  
    printf("x = %0.2f, y = %0.2f", x, y)  
end  
  
~~~  
  
**注意:** Lua 继承类覆盖的方法并不能从 C++ 调用到。也就是说通过 C++ 代码调用这个 cc.Node 对象的 setPosition() 方法时,并不会执行我们在 Lua 中定义的 Toolbar:setPosition() 方法。  
  
@param string classname 类名  
@param [mixed super] 父类或者创建对象实例的函数  
  
@return table  
  
]]  
function class(classname, super)  
    local superType = type(super)  
    local cls  
  
    if superType ~= "function" and superType ~= "table" then  
        superType = nil  
        super = nil  
    end  
  
    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  
  
        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  
        -- 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

传入是一个父类的话,会调用cls.new函数,然后创建实例,调用ctor构造函数

6. 调用一个实例:

假设派生自一个cocos的类 Sprite

-- class可以传1、2个参数  
-- @param 类名,内部做记录而已,一般和返回的类名一致即可  
-- @param 如果传参数2 使用当前函数作为构造函数 如果没参数2 默认的构造函数  
local Box = class("Box", function(filename)  
        return cc.Sprite:create(filename)  
    end)  
  
-- 设置元彪 更改元表默认的元方法  
-- 访问table中不存在的字段时,解释器查找__index的元方法,否则返回nil  
-- 多用于继承 http://blog.csdn.net/q277055799/article/details/8463883  
Box.__index = Box  
Box.isDead = false      --定义属性  
  
-- 构造函数(会自动调用)  
-- 外界构造时可以传任意参数XXX.new(...)  
function Box:ctor(pic_path)  
    local function onNodeEvent(event)  
        if "enter" == event then  
            Box:onEnter(pic_path)  
        elseif "exit" == event then  
            Box:onExit()  
        end  
    end  
  
    self:registerScriptHandler(onNodeEvent)  
  
    local function onUpdate()  
  
    end  
    self:scheduleUpdateWithPriorityLua(onUpdate, 0)  
  
end  
  
function Box:onEnter(pic_path)  
end  
  
function Box:onExit()  
end  
  
  
function Box.create(parent, position)  
    local box = Box.New("data/box.png")  
    parent:addChild(box)  
    return box  
end  
  
return Box
-- class可以传1、2个参数  
-- @param 类名,内部做记录而已,一般和返回的类名一致即可  
-- @param 如果传参数2 使用当前函数作为构造函数 如果没参数2 默认的构造函数  
local Box = class("Box", function(filename)  
        return cc.Sprite:create(filename)  
    end)  
  
-- 设置元彪 更改元表默认的元方法  
-- 访问table中不存在的字段时,解释器查找__index的元方法,否则返回nil  
-- 多用于继承 http://blog.csdn.net/q277055799/article/details/8463883  
Box.__index = Box  
Box.isDead = false      --定义属性  
  
-- 构造函数(会自动调用)  
-- 外界构造时可以传任意参数XXX.new(...)  
function Box:ctor(pic_path)  
    local function onNodeEvent(event)  
        if "enter" == event then  
            Box:onEnter(pic_path)  
        elseif "exit" == event then  
            Box:onExit()  
        end  
    end  
  
    self:registerScriptHandler(onNodeEvent)  
  
    local function onUpdate()  
  
    end  
    self:scheduleUpdateWithPriorityLua(onUpdate, 0)  
  
end  
  
function Box:onEnter(pic_path)  
end  
  
function Box:onExit()  
end  
  
  
function Box.create(parent, position)  
    local box = Box.New("data/box.png")  
    parent:addChild(box)  
    return box  
end  
  
return Box

如果是一个table,可以直接使用

local Bomb = class("Bomb") 
7.我们常见cocos2dx的例子中有大量的extend和tolua.getpeer用法如下:
local TimelineTestScene = class("TimelineTestScene")  
TimelineTestScene.__index = TimelineTestScene  
  
function TimelineTestScene.extend(target)  
    local t = tolua.getpeer(target)  
    if not t then  
        t = {}  
        tolua.setpeer(target, t)  
    end  
    setmetatable(t, TimelineTestScene)  
    return target  
end  
  
function TimelineTestScene.create()  
    local scene = TimelineTestScene.extend(cc.Scene:create())  
    return scene     
end

用的时tolua.getpeer,其实它的功能就相当于调用了class,所以请远离extend吧

local TimelineTestScene = class("TimelineTestScene", cc.Scene)  
TimelineTestScene.__index = TimelineTestScene  

原文

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,491评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,856评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,745评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,196评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,073评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,112评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,531评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,215评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,485评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,578评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,356评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,215评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,583评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,898评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,174评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,497评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,697评论 2 335

推荐阅读更多精彩内容