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

注:文章出处   http://blog.csdn.net/teng_ontheway/article/details/38900211

1.lua中的类

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

2.lua中类的属性

   classA = {width =10, height=10}

   classA={}

   classA.width=10

   classA.height=10 

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

3.类方法

[cpp]  view plain copy
  1. function Box:collsion()  
  2.     -- 默认第一个参数隐藏传递self,可以通过self.xxx 调用属性和方法  
  3. end  
  4.   
  5. function Box.create(self)  
  6.     --必须手动传递参数self,否则无法用self.xxx调用属性和方法  
  7. end  
[cpp]  view plain  copy
  1. function Box:collsion()  
  2.     -- 默认第一个参数隐藏传递self,可以通过self.xxx 调用属性和方法  
  3. end  
  4.   
  5. function Box.create(self)  
  6.     --必须手动传递参数self,否则无法用self.xxx调用属性和方法  
  7. end  
函数的声明和调用可以用":"和".",属性调用全部用点"."


4.类与元表的用法

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

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

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

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

例如:

[cpp]  view plain copy
  1. father = {  
  2.     house=1  
  3. }  
  4. son = {  
  5.     car=1  
  6. }  
  7. setmetatable(son, father) --把son的metatable设置为father  
  8. print(son.house)  
[cpp]  view plain  copy
  1. father = {  
  2.     house=1  
  3. }  
  4. son = {  
  5.     car=1  
  6. }  
  7. setmetatable(son, father) --把son的metatable设置为father  
  8. print(son.house)  
输出结果是nil,如果代码改为

[cpp]  view plain copy
  1. father = {  
  2.     house=1  
  3. }  
  4. father.__index = father -- 把father的__index方法指向自己  
  5. son = {  
  6.     car=1  
  7. }  
  8. setmetatable(son, father)  
  9. print(son.house)  
[cpp]  view plain  copy
  1. father = {  
  2.     house=1  
  3. }  
  4. father.__index = father -- 把father的__index方法指向自己  
  5. son = {  
  6.     car=1  
  7. }  
  8. setmetatable(son, father)  
  9. print(son.house)  
输出的结果就为1了

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

[cpp]  view plain copy
  1. local Box = class("Box", function(filename)  
  2.         return cc.Sprite:create(filename)  
  3.     end)  
  4.   
  5. Box.__index = Box  
[cpp]  view plain  copy
  1. local Box = class("Box", function(filename)  
  2.         return cc.Sprite:create(filename)  
  3.     end)  
  4.   
  5. Box.__index = Box  
设置Box的元表的__index方法为自己,当派生类"SmallBox"派生自"Box",如果在SmallBox中查找不到的属性和方法,就检索元表,当然不是直接从元表中直接检索,是检索元表下的__index,如果__index为nil,则返回nil,如果__index是一个表,那么就到__index方法所指的表中查找对应的属性和方法

具体可以参考:Lua查找表元素过程(元表、__index方法是如何工作的)

5.Cocos2dx中的类

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

[cpp]  view plain copy
  1. --[[--  
  2.   
  3. 创建一个类  
  4.   
  5. ~~~ lua  
  6.   
  7. -- 定义名为 Shape 的基础类  
  8. local Shape = class("Shape")  
  9.   
  10. -- ctor() 是类的构造函数,在调用 Shape.new() 创建 Shape 对象实例时会自动执行  
  11. function Shape:ctor(shapeName)  
  12.     self.shapeName = shapeName  
  13.     printf("Shape:ctor(%s)", self.shapeName)  
  14. end  
  15.   
  16. -- 为 Shape 定义个名为 draw() 的方法  
  17. function Shape:draw()  
  18.     printf("draw %s", self.shapeName)  
  19. end  
  20.   
  21. --  
  22.   
  23. -- Circle 是 Shape 的继承类  
  24. local Circle = class("Circle", Shape)  
  25.   
  26. function Circle:ctor()  
  27.     -- 如果继承类覆盖了 ctor() 构造函数,那么必须手动调用父类构造函数  
  28.     -- 类名.super 可以访问指定类的父类  
  29.     Circle.super.ctor(self, "circle")  
  30.     self.radius = 100  
  31. end  
  32.   
  33. function Circle:setRadius(radius)  
  34.     self.radius = radius  
  35. end  
  36.   
  37. -- 覆盖父类的同名方法  
  38. function Circle:draw()  
  39.     printf("draw %s, raidus = %0.2f", self.shapeName, self.raidus)  
  40. end  
  41.   
  42. --  
  43.   
  44. local Rectangle = class("Rectangle", Shape)  
  45.   
  46. function Rectangle:ctor()  
  47.     Rectangle.super.ctor(self, "rectangle")  
  48. end  
  49.   
  50. --  
  51.   
  52. local circle = Circle.new()             -- 输出: Shape:ctor(circle)  
  53. circle:setRaidus(200)  
  54. circle:draw()                           -- 输出: draw circle, radius = 200.00  
  55.   
  56. local rectangle = Rectangle.new()       -- 输出: Shape:ctor(rectangle)  
  57. rectangle:draw()                        -- 输出: draw rectangle  
  58.   
  59. ~~~  
  60.   
  61. ### 高级用法   
  62.   
  63. class() 除了定义纯 Lua 类之外,还可以从 C++ 对象继承类。  
  64.   
  65. 比如需要创建一个工具栏,并在添加按钮时自动排列已有的按钮,那么我们可以使用如下的代码:  
  66.   
  67. ~~~ lua  
  68.   
  69. -- 从 cc.Node 对象派生 Toolbar 类,该类具有 cc.Node 的所有属性和行为  
  70. local Toolbar = class("Toolbar", function()  
  71.     return display.newNode() -- 返回一个 cc.Node 对象  
  72. end)  
  73.   
  74. -- 构造函数  
  75. function Toolbar:ctor()  
  76.     self.buttons = {} -- 用一个 table 来记录所有的按钮  
  77. end  
  78.   
  79. -- 添加一个按钮,并且自动设置按钮位置  
  80. function Toolbar:addButton(button)  
  81.     -- 将按钮对象加入 table  
  82.     self.buttons[#self.buttons + 1] = button  
  83.   
  84.     -- 添加按钮对象到 cc.Node 中,以便显示该按钮  
  85.     -- 因为 Toolbar 是从 cc.Node 继承的,所以可以使用 addChild() 方法  
  86.     self:addChild(button)  
  87.   
  88.     -- 按照按钮数量,调整所有按钮的位置  
  89.     local x = 0  
  90.     for _, button in ipairs(self.buttons) do  
  91.         button:setPosition(x, 0)  
  92.         -- 依次排列按钮,每个按钮之间间隔 10 点  
  93.         x = x + button:getContentSize().width + 10  
  94.     end  
  95. end  
  96.   
  97. ~~~  
  98.   
  99. class() 的这种用法让我们可以在 C++ 对象基础上任意扩展行为。  
  100.   
  101. 既然是继承,自然就可以覆盖 C++ 对象的方法:  
  102.   
  103. ~~~ lua  
  104.   
  105. function Toolbar:setPosition(x, y)  
  106.     -- 由于在 Toolbar 继承类中覆盖了 cc.Node 对象的 setPosition() 方法  
  107.     -- 所以我们要用以下形式才能调用到 cc.Node 原本的 setPosition() 方法  
  108.     getmetatable(self).setPosition(self, x, y)  
  109.   
  110.     printf("x = %0.2f, y = %0.2f", x, y)  
  111. end  
  112.   
  113. ~~~  
  114.   
  115. **注意:** Lua 继承类覆盖的方法并不能从 C++ 调用到。也就是说通过 C++ 代码调用这个 cc.Node 对象的 setPosition() 方法时,并不会执行我们在 Lua 中定义的 Toolbar:setPosition() 方法。  
  116.   
  117. @param string classname 类名  
  118. @param [mixed super] 父类或者创建对象实例的函数  
  119.   
  120. @return table  
  121.   
  122. ]]  
  123. function class(classname, super)  
  124.     local superType = type(super)  
  125.     local cls  
  126.   
  127.     if superType ~= "function" and superType ~= "table" then  
  128.         superType = nil  
  129.         super = nil  
  130.     end  
  131.   
  132.     if superType == "function" or (super and super.__ctype == 1) then  
  133.         -- inherited from native C++ Object  
  134.         cls = {}  
  135.   
  136.         if superType == "table" then  
  137.             -- copy fields from super  
  138.             for k,v in pairs(super) do cls[k] = v end  
  139.             cls.__create = super.__create  
  140.             cls.super    = super  
  141.         else  
  142.             cls.__create = super  
  143.             cls.ctor = function() end  
  144.         end  
  145.   
  146.         cls.__cname = classname  
  147.         cls.__ctype = 1  
  148.   
  149.         function cls.new(...)  
  150.             local instance = cls.__create(...)  
  151.             -- copy fields from class to native object  
  152.             for k,v in pairs(cls) do instance[k] = v end  
  153.             instance.class = cls  
  154.             instance:ctor(...)  
  155.             return instance  
  156.         end  
  157.   
  158.     else  
  159.         -- inherited from Lua Object  
  160.         if super then  
  161.             cls = {}  
  162.             setmetatable(cls, {__index = super})  
  163.             cls.super = super  
  164.         else  
  165.             cls = {ctor = function() end}  
  166.         end  
  167.   
  168.         cls.__cname = classname  
  169.         cls.__ctype = 2 -- lua  
  170.         cls.__index = cls  
  171.   
  172.         function cls.new(...)  
  173.             local instance = setmetatable({}, cls)  
  174.             instance.class = cls  
  175.             instance:ctor(...)  
  176.             return instance  
  177.         end  
  178.     end  
  179.   
  180.     return cls  
  181. end  
[cpp]  view plain  copy
  1. --[[--  
  2.   
  3. 创建一个类  
  4.   
  5. ~~~ lua  
  6.   
  7. -- 定义名为 Shape 的基础类  
  8. local Shape = class("Shape")  
  9.   
  10. -- ctor() 是类的构造函数,在调用 Shape.new() 创建 Shape 对象实例时会自动执行  
  11. function Shape:ctor(shapeName)  
  12.     self.shapeName = shapeName  
  13.     printf("Shape:ctor(%s)", self.shapeName)  
  14. end  
  15.   
  16. -- 为 Shape 定义个名为 draw() 的方法  
  17. function Shape:draw()  
  18.     printf("draw %s", self.shapeName)  
  19. end  
  20.   
  21. --  
  22.   
  23. -- Circle 是 Shape 的继承类  
  24. local Circle = class("Circle", Shape)  
  25.   
  26. function Circle:ctor()  
  27.     -- 如果继承类覆盖了 ctor() 构造函数,那么必须手动调用父类构造函数  
  28.     -- 类名.super 可以访问指定类的父类  
  29.     Circle.super.ctor(self, "circle")  
  30.     self.radius = 100  
  31. end  
  32.   
  33. function Circle:setRadius(radius)  
  34.     self.radius = radius  
  35. end  
  36.   
  37. -- 覆盖父类的同名方法  
  38. function Circle:draw()  
  39.     printf("draw %s, raidus = %0.2f", self.shapeName, self.raidus)  
  40. end  
  41.   
  42. --  
  43.   
  44. local Rectangle = class("Rectangle", Shape)  
  45.   
  46. function Rectangle:ctor()  
  47.     Rectangle.super.ctor(self, "rectangle")  
  48. end  
  49.   
  50. --  
  51.   
  52. local circle = Circle.new()             -- 输出: Shape:ctor(circle)  
  53. circle:setRaidus(200)  
  54. circle:draw()                           -- 输出: draw circle, radius = 200.00  
  55.   
  56. local rectangle = Rectangle.new()       -- 输出: Shape:ctor(rectangle)  
  57. rectangle:draw()                        -- 输出: draw rectangle  
  58.   
  59. ~~~  
  60.   
  61. ### 高级用法  
  62.   
  63. class() 除了定义纯 Lua 类之外,还可以从 C++ 对象继承类。  
  64.   
  65. 比如需要创建一个工具栏,并在添加按钮时自动排列已有的按钮,那么我们可以使用如下的代码:  
  66.   
  67. ~~~ lua  
  68.   
  69. -- 从 cc.Node 对象派生 Toolbar 类,该类具有 cc.Node 的所有属性和行为  
  70. local Toolbar = class("Toolbar", function()  
  71.     return display.newNode() -- 返回一个 cc.Node 对象  
  72. end)  
  73.   
  74. -- 构造函数  
  75. function Toolbar:ctor()  
  76.     self.buttons = {} -- 用一个 table 来记录所有的按钮  
  77. end  
  78.   
  79. -- 添加一个按钮,并且自动设置按钮位置  
  80. function Toolbar:addButton(button)  
  81.     -- 将按钮对象加入 table  
  82.     self.buttons[#self.buttons + 1] = button  
  83.   
  84.     -- 添加按钮对象到 cc.Node 中,以便显示该按钮  
  85.     -- 因为 Toolbar 是从 cc.Node 继承的,所以可以使用 addChild() 方法  
  86.     self:addChild(button)  
  87.   
  88.     -- 按照按钮数量,调整所有按钮的位置  
  89.     local x = 0  
  90.     for _, button in ipairs(self.buttons) do  
  91.         button:setPosition(x, 0)  
  92.         -- 依次排列按钮,每个按钮之间间隔 10 点  
  93.         x = x + button:getContentSize().width + 10  
  94.     end  
  95. end  
  96.   
  97. ~~~  
  98.   
  99. class() 的这种用法让我们可以在 C++ 对象基础上任意扩展行为。  
  100.   
  101. 既然是继承,自然就可以覆盖 C++ 对象的方法:  
  102.   
  103. ~~~ lua  
  104.   
  105. function Toolbar:setPosition(x, y)  
  106.     -- 由于在 Toolbar 继承类中覆盖了 cc.Node 对象的 setPosition() 方法  
  107.     -- 所以我们要用以下形式才能调用到 cc.Node 原本的 setPosition() 方法  
  108.     getmetatable(self).setPosition(self, x, y)  
  109.   
  110.     printf("x = %0.2f, y = %0.2f", x, y)  
  111. end  
  112.   
  113. ~~~  
  114.   
  115. **注意:** Lua 继承类覆盖的方法并不能从 C++ 调用到。也就是说通过 C++ 代码调用这个 cc.Node 对象的 setPosition() 方法时,并不会执行我们在 Lua 中定义的 Toolbar:setPosition() 方法。  
  116.   
  117. @param string classname 类名  
  118. @param [mixed super] 父类或者创建对象实例的函数  
  119.   
  120. @return table  
  121.   
  122. ]]  
  123. function class(classname, super)  
  124.     local superType = type(super)  
  125.     local cls  
  126.   
  127.     if superType ~= "function" and superType ~= "table" then  
  128.         superType = nil  
  129.         super = nil  
  130.     end  
  131.   
  132.     if superType == "function" or (super and super.__ctype == 1) then  
  133.         -- inherited from native C++ Object  
  134.         cls = {}  
  135.   
  136.         if superType == "table" then  
  137.             -- copy fields from super  
  138.             for k,v in pairs(super) do cls[k] = v end  
  139.             cls.__create = super.__create  
  140.             cls.super    = super  
  141.         else  
  142.             cls.__create = super  
  143.             cls.ctor = function() end  
  144.         end  
  145.   
  146.         cls.__cname = classname  
  147.         cls.__ctype = 1  
  148.   
  149.         function cls.new(...)  
  150.             local instance = cls.__create(...)  
  151.             -- copy fields from class to native object  
  152.             for k,v in pairs(cls) do instance[k] = v end  
  153.             instance.class = cls  
  154.             instance:ctor(...)  
  155.             return instance  
  156.         end  
  157.   
  158.     else  
  159.         -- inherited from Lua Object  
  160.         if super then  
  161.             cls = {}  
  162.             setmetatable(cls, {__index = super})  
  163.             cls.super = super  
  164.         else  
  165.             cls = {ctor = function() end}  
  166.         end  
  167.   
  168.         cls.__cname = classname  
  169.         cls.__ctype = 2 -- lua  
  170.         cls.__index = cls  
  171.   
  172.         function cls.new(...)  
  173.             local instance = setmetatable({}, cls)  
  174.             instance.class = cls  
  175.             instance:ctor(...)  
  176.             return instance  
  177.         end  
  178.     end  
  179.   
  180.     return cls  
  181. end  

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

6. 调用一个实例:

假设派生自一个cocos的类 Sprite

[cpp]  view plain copy
  1. -- class可以传1、2个参数  
  2. -- @param 类名,内部做记录而已,一般和返回的类名一致即可  
  3. -- @param 如果传参数2 使用当前函数作为构造函数 如果没参数2 默认的构造函数  
  4. local Box = class("Box", function(filename)  
  5.         return cc.Sprite:create(filename)  
  6.     end)  
  7.   
  8. -- 设置元彪 更改元表默认的元方法  
  9. -- 访问table中不存在的字段时,解释器查找__index的元方法,否则返回nil  
  10. -- 多用于继承 http://blog.csdn.net/q277055799/article/details/8463883  
  11. Box.__index = Box  
  12. Box.isDead = false      --定义属性  
  13.   
  14. -- 构造函数(会自动调用)  
  15. -- 外界构造时可以传任意参数XXX.new(...)  
  16. function Box:ctor(pic_path)  
  17.     local function onNodeEvent(event)  
  18.         if "enter" == event then  
  19.             Box:onEnter(pic_path)  
  20.         elseif "exit" == event then  
  21.             Box:onExit()  
  22.         end  
  23.     end  
  24.   
  25.     self:registerScriptHandler(onNodeEvent)  
  26.   
  27.     local function onUpdate()  
  28.   
  29.     end  
  30.     self:scheduleUpdateWithPriorityLua(onUpdate, 0)  
  31.   
  32. end  
  33.   
  34. function Box:onEnter(pic_path)  
  35. end  
  36.   
  37. function Box:onExit()  
  38. end  
  39.   
  40.   
  41. function Box.create(parent, position)  
  42.     local box = Box.New("data/box.png")  
  43.     parent:addChild(box)  
  44.     return box  
  45. end  
  46.   
  47. return Box  
[cpp]  view plain  copy
  1. -- class可以传1、2个参数  
  2. -- @param 类名,内部做记录而已,一般和返回的类名一致即可  
  3. -- @param 如果传参数2 使用当前函数作为构造函数 如果没参数2 默认的构造函数  
  4. local Box = class("Box", function(filename)  
  5.         return cc.Sprite:create(filename)  
  6.     end)  
  7.   
  8. -- 设置元彪 更改元表默认的元方法  
  9. -- 访问table中不存在的字段时,解释器查找__index的元方法,否则返回nil  
  10. -- 多用于继承 http://blog.csdn.net/q277055799/article/details/8463883  
  11. Box.__index = Box  
  12. Box.isDead = false      --定义属性  
  13.   
  14. -- 构造函数(会自动调用)  
  15. -- 外界构造时可以传任意参数XXX.new(...)  
  16. function Box:ctor(pic_path)  
  17.     local function onNodeEvent(event)  
  18.         if "enter" == event then  
  19.             Box:onEnter(pic_path)  
  20.         elseif "exit" == event then  
  21.             Box:onExit()  
  22.         end  
  23.     end  
  24.   
  25.     self:registerScriptHandler(onNodeEvent)  
  26.   
  27.     local function onUpdate()  
  28.   
  29.     end  
  30.     self:scheduleUpdateWithPriorityLua(onUpdate, 0)  
  31.   
  32. end  
  33.   
  34. function Box:onEnter(pic_path)  
  35. end  
  36.   
  37. function Box:onExit()  
  38. end  
  39.   
  40.   
  41. function Box.create(parent, position)  
  42.     local box = Box.New("data/box.png")  
  43.     parent:addChild(box)  
  44.     return box  
  45. end  
  46.   
  47. return Box  

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

[cpp]  view plain copy
  1. local Bomb = class("Bomb")  
[cpp]  view plain  copy
  1. local Bomb = class("Bomb")  


7.我们常见cocos2dx的例子中有大量的extend和tolua.getpeer用法如下:

[cpp]  view plain copy
  1. local TimelineTestScene = class("TimelineTestScene")  
  2. TimelineTestScene.__index = TimelineTestScene  
  3.   
  4. function TimelineTestScene.extend(target)  
  5.     local t = tolua.getpeer(target)  
  6.     if not t then  
  7.         t = {}  
  8.         tolua.setpeer(target, t)  
  9.     end  
  10.     setmetatable(t, TimelineTestScene)  
  11.     return target  
  12. end  
  13.   
  14. function TimelineTestScene.create()  
  15.     local scene = TimelineTestScene.extend(cc.Scene:create())  
  16.     return scene     
  17. end  
[cpp]  view plain  copy
  1. local TimelineTestScene = class("TimelineTestScene")  
  2. TimelineTestScene.__index = TimelineTestScene  
  3.   
  4. function TimelineTestScene.extend(target)  
  5.     local t = tolua.getpeer(target)  
  6.     if not t then  
  7.         t = {}  
  8.         tolua.setpeer(target, t)  
  9.     end  
  10.     setmetatable(t, TimelineTestScene)  
  11.     return target  
  12. end  
  13.   
  14. function TimelineTestScene.create()  
  15.     local scene = TimelineTestScene.extend(cc.Scene:create())  
  16.     return scene     
  17. end  

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

[cpp]  view plain copy
  1. local TimelineTestScene = class("TimelineTestScene", cc.Scene)  
  2. TimelineTestScene.__index = TimelineTestScene  
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值