美文网首页
Lua继承原理(如何实现面向对象)

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

作者: George2016 | 来源:发表于2017-08-31 14:56 被阅读530次
    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  
    

    原文

    相关文章

      网友评论

          本文标题:Lua继承原理(如何实现面向对象)

          本文链接:https://www.haomeiwen.com/subject/gzeqjxtx.html