lua面向对象类,继承和多重继承的实现
2018-02-01 17:29
519 查看
语法糖 在讨论lua脚本的面向对象实现之前,我们先了解一个概念“语法糖(syntactic sugar)”,百度官方的解释是:
也译为糖衣语法,是由英国计算机科学家彼得·约翰·兰达(Peter J. Landin)发明的一个术语,指计算机语言中添加的某种语法,这种语法对语言的功能并没有影响,但是更方便程序员使用。通常来说使用语法糖能够增加程序的可读性,从而减少程序代码出错的机会
官方的东西一般都比较专业和严谨,但不好理解;我的看法就是语法糖是某种语法的别名,只是这个别名背地里多做了些事情,所以我们用的时候就不用那么麻烦。某种程度上可以理解为宏,只是宏定义多了做了些事情。比如下面的宏
好了,这里在提前说明两个lua中用到的标点符号点号”.”和冒号“:”,冒号就是点号的语法糖,冒号比点号在背地里多做了点事情,所以比点号多一个点,^_^。
举例说名相同类的分别用“.”和“:”的实现方式
冒号
点号
两种方法运行的结果相同,如下
从上面两个例子中可以看出,用点号“.”比用冒号“:”的函数参数要多一个self或者是实例本身(object),代码上看显然用冒号的要方便的多,实际冒号也是带了self参数的,只是隐藏了,我们不需要关心。为啥要使用self呢,在lua程序设计第二版中有提到当一项操作所作用的”接受者”,需要一个额外的参数来表示该接受者,这个接受者就是self。
采用冒号写法的函数会多出一个隐藏参数self,这个self不属于Lua的关键字,但在冒号定义下会由编译器自动创建这个局部变量,如果不想用self这个名字,修改lua的源代码重新编译就可以了,只是这样一样,就不能用其它第三方库了。self的意思也很好理解,就是这个table本身,相当于C++的this指针,self指向当前作用域的父表结构,通常在函数定义中使用。
类
我们在来分析一下,lua如何使用元表的定义实现了面向对象编程,实现的方法有很多种,网上搜每一篇的形式都不同,但基本的要点是相同的。就是利用了元表的特性,派生出子类。这里以冒号的代码来说明
下面详细说明,会比较啰嗦
继承
下面看一个改进的版本,比较完整的实例,含父类和子类
执行结果如下
student 是 human的派生类,构造方法new在子类student中可以重载(重写),也可以不重载,父类的方法也是可以重载。这样就实现了继承,借用别人的话:Lua里的继承就是在别人的table里查找自己不存在的字段,这里的字段当然也包含函数,别忘了在lua中函数也是一种类型。
多重继承
下面讲讲多重继承,lua的单继承如果说是在别的表中查找自己没有的东西,那么多重继承就是查找多个表,即在多个表中找到自己需要的。
在介绍多重继承之前,我们先看一幅图,然后分享一个小故事
![](http://img.blog.csdn.net/20180201191101980?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvbGpkNjgw/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
小故事
故事是这样的:话说科技发达了,克隆人小菜一碟,有a和b两个男人,a有钱但身体不好,b身体好但没钱。这两人都找不到老婆又想要小孩,于是找到科学家”大勇”网名流浪先生,想要克隆小孩子。科学家的半吊子徒弟“wuli涛”操作失误,将a和b的基因都丢到了基因组合机器里面,结果克隆了一个混杂的child。这个child继承了father a 和father b的所有东西,但小孩就这么一个,这俩爹都说小孩是自己的,争得头破血流。科学家“流浪先生”没办法又找到叫做new的克隆工厂,把child小孩复制一下,这样就不用争了。于是又克隆出tony和andy两位小朋友,但克隆的时候出了事故,多克隆了一个lucy,这货比较倒霉,father a 和 father b都不要他。注:不要问我child去哪里,有可能这货被科学家徒弟私自藏起来了,没事自己克隆玩^_^。
呵呵,算是自己瞎写的微型小说吧,但这个故事大致说明了lua子类继承多个父类的关系。
执行的结果为:
简单说明一下设计思路
要实现继承多个类,实现的方式就是让父类(table)包含多个需要继承的类(table),然后通过seach来查找匹配;查找的参数是,父类fathers 和 key,这个key如果是子类调用父类的函数,那么就是函数名。
fathers里包含所有需要继承的类,参数用的是…,不定参数形式,可以有多个。
createclass创建child, child和fathers是元表关系,child中没有的东西,都可以去fathers中查找,比如代码child:car(),是在fathers中查找car函数,显然结果是找到了father a有car。
createclass中new的作用是用于创建不同的对象,所以我们可以用child子类去做创建对象(table)的工作,new出多个对象。
多层次继承
我们可以看到lua面向对象的实现最基础的就是元表,利用setmetatable建立子类和父类的关联关系,我们还可以思考一下三层继承关系,即爷爷、父亲、儿子(孙子)的继承实现。
执行结果如下
源文件 class8.lua
以上都是一些简单的例子,再加上自己的一些理解,如果有错,请指正。
多重继承这块,借鉴了别人的设计,附上链接,如有侵权,请留言。
http://www.jb51.net/article/55171.htm
也译为糖衣语法,是由英国计算机科学家彼得·约翰·兰达(Peter J. Landin)发明的一个术语,指计算机语言中添加的某种语法,这种语法对语言的功能并没有影响,但是更方便程序员使用。通常来说使用语法糖能够增加程序的可读性,从而减少程序代码出错的机会
官方的东西一般都比较专业和严谨,但不好理解;我的看法就是语法糖是某种语法的别名,只是这个别名背地里多做了些事情,所以我们用的时候就不用那么麻烦。某种程度上可以理解为宏,只是宏定义多了做了些事情。比如下面的宏
#include <stdio.h> #define ARRAY_LEN(st) do{ assert(st!=NULL); sizeof(st)/sizeof(st[0]) }while(0) // ARRAY_LEN是求数组长度的宏,但我们额外加了assert判断,排除st为非法指针的情况
好了,这里在提前说明两个lua中用到的标点符号点号”.”和冒号“:”,冒号就是点号的语法糖,冒号比点号在背地里多做了点事情,所以比点号多一个点,^_^。
举例说名相同类的分别用“.”和“:”的实现方式
冒号
Class = {} Class.__index = Class function Class:new(x,y) local o = {len=1} setmetatable(o, Class) --[[o.x = x o.y = y--]] self.x = x self.y = y return o end function Class:test() print(self.x,self.y) end object = Class:new(10,20) object:test() object.test(object)
点号
Class = {} Class.__index = Class function Class.new(self,x,y) local o = {len=1} setmetatable(o, Class) --[[o.x = x o.y = y--]] self.x = x self.y = y return o end function Class.test(self) print(self.x,self.y) end object = Class:new(10,20) object:test() object.test(object)
两种方法运行的结果相同,如下
10 20 10 20
从上面两个例子中可以看出,用点号“.”比用冒号“:”的函数参数要多一个self或者是实例本身(object),代码上看显然用冒号的要方便的多,实际冒号也是带了self参数的,只是隐藏了,我们不需要关心。为啥要使用self呢,在lua程序设计第二版中有提到当一项操作所作用的”接受者”,需要一个额外的参数来表示该接受者,这个接受者就是self。
采用冒号写法的函数会多出一个隐藏参数self,这个self不属于Lua的关键字,但在冒号定义下会由编译器自动创建这个局部变量,如果不想用self这个名字,修改lua的源代码重新编译就可以了,只是这样一样,就不能用其它第三方库了。self的意思也很好理解,就是这个table本身,相当于C++的this指针,self指向当前作用域的父表结构,通常在函数定义中使用。
类
我们在来分析一下,lua如何使用元表的定义实现了面向对象编程,实现的方法有很多种,网上搜每一篇的形式都不同,但基本的要点是相同的。就是利用了元表的特性,派生出子类。这里以冒号的代码来说明
Class = {} Class.__index = Class function Class:new(x,y) local o = {len=1} setmetatable(o, Class) --[[o.x = x o.y = y--]] self.x = x self.y = y return o end function Class:test() print(self.x,self.y) end object = Class:new(10,20) object:test()
下面详细说明,会比较啰嗦
Class = {} --定义table,即Class类,没有成员,成员在后面new函数中添加;也可以在此处声明 --如:Class = {x,y}
Class.__index = Class --定义元方法,这个可以放到new函数里面 如 setmetatable(o, {__index=Class}) 或者是 setmetatable(o, Class) self.__index = self -- self可以用Class替代,是一个意思
function Class:new(x,y) --创建对象或子类的构造方法,函数名没有要求,习惯用new local o = {len=1} --子类,可以为空,也可以有自己的成员变量 setmetatable(o, Class) --设置元表,目的是让temp继承Class self.x = x -- 父类新增成员x,并赋值 self.y = y --同上 return o --返回o,此时应当看做是返回类的对象 end --这样就完成了一个类的构造函数,可以用此函数类生成类的对象
function Class:test() print(self.x,self.y) end --[[Class的成员函数, o是Class的对象,打印对象的x和y值--]]
object = Class:new(10,20) --[[创建object对象,赋初值x=10 y=20--]] object:test() --调用成员函数 object.test(object) --显式的调用成员函数,用的是点,需要带入object --这也证明test函数中self指的是调用者,此处是object
继承
下面看一个改进的版本,比较完整的实例,含父类和子类
human = {name="", sex = "", age = 0} --基类,允许为空 function human:new(n, s, a) local o = { name = n, sex = s, age = a } setmetatable(o , {__index = self}) return o end function human:printattri() print(self.name, self.sex, self.age) end --派生类,子类 student = human:new() --无需带参数 function student:new(n,s,a, sc) --重载父类的"构造函数", --也可以不重载,沿用父类human的 local o = { name = n, sex = s, age = a, score = sc } setmetatable(o, {__index = self}) return o end --[[function student:printattri() print(self.name, self.sex, self.age, self.score) end--]] -- 重载 父类的printattri函数 function student:printscore() --子类自有函数 print(self.score) end --创建一个父类对象和子类对象 tony = human:new("Tony", "male", 10) andy = student:new("Andy", "female", 12, 100) --andy = student:new("Andy", "female", 12) tony:printattri() andy:printattri() andy:printscore()
执行结果如下
Tony male 10 Andy female 12 100
student 是 human的派生类,构造方法new在子类student中可以重载(重写),也可以不重载,父类的方法也是可以重载。这样就实现了继承,借用别人的话:Lua里的继承就是在别人的table里查找自己不存在的字段,这里的字段当然也包含函数,别忘了在lua中函数也是一种类型。
多重继承
下面讲讲多重继承,lua的单继承如果说是在别的表中查找自己没有的东西,那么多重继承就是查找多个表,即在多个表中找到自己需要的。
在介绍多重继承之前,我们先看一幅图,然后分享一个小故事
小故事
故事是这样的:话说科技发达了,克隆人小菜一碟,有a和b两个男人,a有钱但身体不好,b身体好但没钱。这两人都找不到老婆又想要小孩,于是找到科学家”大勇”网名流浪先生,想要克隆小孩子。科学家的半吊子徒弟“wuli涛”操作失误,将a和b的基因都丢到了基因组合机器里面,结果克隆了一个混杂的child。这个child继承了father a 和father b的所有东西,但小孩就这么一个,这俩爹都说小孩是自己的,争得头破血流。科学家“流浪先生”没办法又找到叫做new的克隆工厂,把child小孩复制一下,这样就不用争了。于是又克隆出tony和andy两位小朋友,但克隆的时候出了事故,多克隆了一个lucy,这货比较倒霉,father a 和 father b都不要他。注:不要问我child去哪里,有可能这货被科学家徒弟私自藏起来了,没事自己克隆玩^_^。
呵呵,算是自己瞎写的微型小说吧,但这个故事大致说明了lua子类继承多个父类的关系。
function search(classes, key) for i=1, #classes do local value = classes[i][key]; if value ~= nil then return value; end end end function createclass(...) local fathers = {...}; local child = {}; --设置类的元素 setmetatable(child , { __index = function(table, key) return search(fathers, key); end }) --新增new函数,用于创建对象 function child:new() local o = {}; setmetatable(o, {__index=self}); return o; end function child:howl() print("我爸是谁,天知道!"); end --返回继承了多个父类的子类 return child; end father_a = {}; function father_a:car() print("我是你爹,你看我有宝马!拿去用"); end --[[function father_a:new() o = {}; setmetatable(o, {__index=self}); return o; end--]] --这段代码有无对结果都没有影响 father_b = {}; function father_b:bike() print("我是你爹,你看我也有车!自行车给你用"); end --[[function father_b:new() o = {}; setmetatable(o, {__index=father_b}) return o; end--]] local child = createclass(father_a, father_b); print("一个娃,不够分"); child:car(); child:bike(); print("***************************************") local tony = child:new(); local andy = child:new(); local lucy = child:new(); tony:car(); print("tony的老爹有钱有汽车"); andy:bike(); print("andy的老爹没钱有自行车"); lucy:howl(); print("lucy比较悲催");
执行的结果为:
一个娃,不够分 我是你爹,你看我有宝马!拿去用 我是你爹,你看我也有车!自行车给你用 *************************************** 我是你爹,你看我有宝马!拿去用 tony的老爹有钱有汽车 我是你爹,你看我也有车!自行车给你用 andy的老爹没钱有自行车 我爸是谁,天知道! lucy比较悲催
简单说明一下设计思路
要实现继承多个类,实现的方式就是让父类(table)包含多个需要继承的类(table),然后通过seach来查找匹配;查找的参数是,父类fathers 和 key,这个key如果是子类调用父类的函数,那么就是函数名。
fathers里包含所有需要继承的类,参数用的是…,不定参数形式,可以有多个。
createclass创建child, child和fathers是元表关系,child中没有的东西,都可以去fathers中查找,比如代码child:car(),是在fathers中查找car函数,显然结果是找到了father a有car。
createclass中new的作用是用于创建不同的对象,所以我们可以用child子类去做创建对象(table)的工作,new出多个对象。
多层次继承
我们可以看到lua面向对象的实现最基础的就是元表,利用setmetatable建立子类和父类的关联关系,我们还可以思考一下三层继承关系,即爷爷、父亲、儿子(孙子)的继承实现。
grandpa = {name="", sex = "", age = 0} --基类,允许为空 function grandpa:new(n, s, a) local o = { name = n, sex = s, age = a } setmetatable(o , {__index = self}) return o end function grandpa:printattri() print(self.name, self.sex, self.age) end --派生类 father = grandpa:new() --无需带参数 function father:new(n,s,a,w) local o = { name = n, sex = s, age = a, wife = w } setmetatable(o, {__index = self}) return o end function father:printwife() print("wife is "..self.wife) end son = father:new() function son:new(n,s,a,f) local o = { name = n, sex = s, age = a, friend = f } setmetatable(o, {__index = self}) return o end function son:printfriend() print("friend is "..self.friend) end --创建一个父类对象和派生类对象 tony = grandpa:new("Tony", "male", 60) andy = father:new("Andy", "female", 30,"helen") lucy = son:new("Andy", "female", 10,"lilei") tony:printattri() -- 爷爷的方法,爷爷调用没问题 andy:printattri() -- 爷爷的方法,父亲调用没有问题 andy:printwife() -- 父亲的方法,父亲调用没有问题 lucy:printattri() -- 爷爷的方法,孙子调用没有问题 lucy:printfriend() -- 孙子的方法,孙子调用没有问题 lucy:printwife() -- 父亲的方法,儿子调用,没有老婆,打印输出nil andy:printfiend() -- andy老爹没有朋友属性,也没有printfriend方法, -- 调用儿子的printfriend,系统报错误
执行结果如下
源文件 class8.lua
Tony male 60 Andy female 30 wife is helen Andy female 10 friend is lilei lua: class8.lua:30: attempt to concatenate field 'wife' (a nil value) stack traceback: class8.lua:30: in function 'printwife' class8.lua:61: in main chunk [C]: ?
以上都是一些简单的例子,再加上自己的一些理解,如果有错,请指正。
多重继承这块,借鉴了别人的设计,附上链接,如有侵权,请留言。
http://www.jb51.net/article/55171.htm
相关文章推荐
- lua面向对象实现(实例化对象、继承、多态、多继承、单例模式)
- LUA面向对象程序设计(四)多重继承
- lua面向对象实现-类实例化对象、继承、多态、多继承、lua单例模式
- 【游戏开发】在Lua中实现面向对象特性——模拟类、继承、多态
- lua面向对象实现-类实例化对象、继承、多态、多继承、lua单例模式
- lua面向对象实现-类实例化对象、继承、多态、多继承、lua单例模式
- lua面向对象实现-类实例化对象、继承、多态、多继承、lua单例模式
- Lua程序设计(三)面向对象实现一个简单的类
- lua中的面向对象模拟,类,继承,多态
- 面向对象思考及go实现继承与多态
- Lua查找表元素过程(元表、__index方法是如何工作的 , 后续需整理一下实现多重继承的思路)
- Javascript乱弹设计模式系列(0) - 面向对象基础以及接口和继承类的实现
- java面向对象一:实现继承、重载、重写
- day32--面向对象的程序设计之继承实现的原理(继承顺序)、封装、property
- lua的面向对象实现
- Lua笔记19 面向对象实现__index
- lua中的面向对象模拟,类,继承,多态
- lua面向对象实现的(记)
- Lua面向对象实现
- 面向对象的程序设计——实现继承