学习Cocos2d-x Lua:面向对象 -- 多重继承、私密性

2015年03月25日 11:27 0 点赞 0 评论 更新于 2025-11-21 18:31

在这个系列中,我们主要聚焦于学习Cocos2d-x Lua,总结Lua开发过程中涉及的知识点,以及探讨如何在开发过程中使用Cocos Code IDE。本篇文章将深入讲解Lua面向对象编程中的多重继承和私密性,包括如何在多个类中查找一个字段、创建继承多个类的子类,以及实现类的私密性。

1. 多重继承之在多个类中查找一个字段

多重继承本身并不复杂,除非两个将要被继承的类存在相同的函数名和属性,否则处理起来相对简单。其实质就是在多个table中查找某个字段,因为在Lua里,继承的实现方式就是在其他table中查找自身不存在的字段。

单继承与多重继承的区别在于,单继承只查找一个table,而多重继承需要查找两个或以上的table。下面我们来看看如何从2个或多个table中查找某个字段,示例代码如下:

function search(classes, key)
for i = 1, #classes do
local value = classes[i][key];
if value ~= nil then
return value;
end
end
end
local t1 = {name = "hehe"};
local t2 = {game = "who"};
print(search({t1, t2}, "game"));

在上述代码中,classes 参数是一个table,其中存放了多个table,这些table代表了我们想要继承的类。key 则是要查找的字段。代码通过遍历所有的table,判断该字段是否存在于某个table中,若找到则返回该值。

测试代码是从 t1t2 中查找 game 字段,这里的 t1t2 可看作两个类。运行结果如下:

[LUA-print] who

2. 多重继承之创建继承多个类的子类

前面的 search 函数相对简单,它只是一个预热。真正创建多重继承的函数会复杂一些,示例代码如下:

function createClass(...)
local parents = {...};
local child = {};

-- 设置类的元表
setmetatable(child, {
__index = function(table, key)
return search(parents, key);
end
})

-- 给类新增一个new函数,用于创建对象
function child:new()
o = {};
setmetatable(o, child);
child.__index = child;
return o;
end

-- 返回这个继承了多个类的子类
return child;
end

createClass 函数用于创建一个继承了多个类的子类,下面对其进行详细分析:

  1. 参数:该函数接受可变参数,我们需要将多个被继承的类作为参数传递进来。
  2. 保存父类parents 用于保存这些被继承的类。
  3. 创建子类:创建一个新的table child,它就是我们想要的继承了多个类的子类。
  4. 设置元表和 __index 元方法:给 child 设置元表,并设置 __index 元方法。当 __index 是一个函数时,其参数为元表所属的table和要查找的字段名。
  5. 实现继承:在 __index 元方法函数里调用 search 函数,从多个父类中查找所需的字段。这样,当调用 child 的某个函数时,就会从各个父类中查找,从而完成继承的工作。
  6. 创建对象的 new 函数new 函数用于创建 child 的子类,实现方式与之前文章中所述相同。若忘记相关内容,可查看这篇文章
  7. 返回子类:最后返回 child,完成子类的创建。

虽然看起来复杂,但实际上还是对 __index 的应用。下面是测试代码:

-- 一个精灵类
TSprite = {}
function TSprite:hello()
print("谁跟你hello!");
end

function TSprite:new()
o = {}
setmetatable(o, self);
self.__index = self;
return o;
end

-- 一个子弹类
TBullet = {}
function TBullet:fire()
print("别动,再动我就瞄不准了!");
end
function TBullet:new()
o = {}
setmetatable(o, self);
self.__index = self;
return o;
end

-- 继承了两个类的子类
local BulletSprite = createClass(TSprite, TBullet);

-- 子类的对象
local bSprite = BulletSprite:new();
bSprite:hello();
bSprite:fire();

上述代码创建了两个类 TSpriteTBullet,然后调用 createClass 函数创建一个继承了这两个类的子类 BulletSprite,最后创建子类的对象并调用其 hellofire 函数。运行结果如下:

[LUA-print] 谁跟你hello!
[LUA-print] 别动,再动我就瞄不准了!

3. 类的私密性

接下来介绍一个与多重继承无关的技巧——类的私密性。在Java、C++等语言中,我们熟悉 publicprivateprotected 等关键词,这些关键词使得封装成为可能。然而,在Lua里并没有私密的概念,类本质上是一个table,table的所有字段都可以被调用,不存在公有和私有之分。

如果希望某些函数和属性不被外部调用,也可以实现,但实现方式可能看起来有些别扭,示例代码如下:

function createTSprite()
local self = {name = "benmutou"};
local function myBus()
print("myBus是我自己的函数,你不能直接调用");
end
local function myGame()
print("myGame是我自己的函数,你不能直接调用");
end
local function hello()
print("hello:");
myBus();
end
local function hi()
print("hi:");
myGame();
end

local function setName(newName)
self.name = newName;
end

return {hello = hello, hi = hi, setName = setName};
end

在上述代码中,我们不再使用冒号来定义函数。类中的 namemyBusmyGame 不希望被外部直接调用。调用 createTSprite 函数后,会返回一个新的table,该table仅存放了能够被外部直接调用的函数或属性。

下面是测试代码:

local sp = createTSprite();
sp.hello();
sp.hi();

运行结果如下:

[LUA-print] hello:
[LUA-print] myBus是我自己的函数,你不能直接调用
[LUA-print] hi:
[LUA-print] myGame是我自己的函数,你不能直接调用

通过这种方式,我们创建的对象只能使用 hellohisetName 函数,而其他的 namemyBusmyGame 只能通过这些可使用的函数间接调用,从而实现了类的私密性。不过,这种实现方式可能会让人感觉不太像传统意义上的类。

4. 总结

关于面向对象的内容,本文暂时介绍到这里。文中内容可能较为基础,目的是为了巩固Lua基础。后续我们将继续深入探讨Cocos2d-x Lua的相关知识。

作者信息

boke

boke

共发布了 3994 篇文章