lua的面向对象编程,封装,继承,多态的实现

简介

1. lua面向对象编程是基于元表metatable,元方法__index来实现的,具体元表和元方法的介绍

    请见Lua的元表metatable及元方法

2. 语法糖

    语法糖是由英国计算机科学家彼得·约翰·兰达(Peter J. Landin)发明的一个术语,指计算机语言中添加的某种语法,这种语法对语言的功能并没有影响,但是更方便程序员使用

    举例说明:数组char test[100],如果要访问第11个元素,可以这样写:char c = *(test+10),但是用语法糖的话就简单了,直接char c = test[10],看到没?语法糖就是一些简便写法

    lua中的语法糖

    lua中的函数默认都是有self传递进去的,self相当于C++类中函数的this指针,语法糖会自动给我们传递 self

    举例说明:

local a =
{
	x = 99
}

-- 打印函数,注意这里要访问表a中的变量x,必须指明self.x或者a.x,不然会报错
function a:Print()
	print("function a:test() " ..self.x)
end

-- 想调用a的Print()函数,我们可以这样写,注意参数是a,否则调用出错
a.Print(a)

-- 也可以这样写,即用:代替. 且不用传入参数a
a:Print()

    明显第二种方法更简便

2. lua面向对象的原理(基于元表metatable和元方法__index)

    如果访问了lua表中不存在的元素时,就会触发lua的一套查找机制,也是凭借这个机制,才能够实现面向对象的

    举例说明:

test =
{
}

-- 访问表test中不存在的变量a
print(test.a)

    打印结果:nil

    原因很简单:表test中不存在变量a,所以打印为nil,但是如果表test有元表metatable的话,情况就不一样了

    元表像是一个备用查找表,假设表A的元表是B,那么在A中找不到的东西就会尝试在B中去找,设置元表的函数如下

    setmetatable(A, B),这样表B就被设置为A的元表,当A中查找不到某个变量时就会到B中进行查找

    举例说明:

-- 表A
A =
{
}

-- 表B
B =
{
	a = 99
}

-- 设置表B为表A的元表
setmetatable(A,B)

-- 再访问表A中不存在的变量a
print(A.a)

    打印结果依然为:nil

    why?因为表B 的元方法__index没有赋值。按照笔者的理解,元方法__index是用来确定一个表在被作为元表时的查找方法

    我们做如下更改,即对表B的元方法进行赋值

    代码如下:

-- 表A
A =
{
}

-- 表B
B =
{
	a = 99
}

-- 给表B的元方法__index进行赋值
B.__index = B

-- 设置表B为表A的元表
setmetatable(A,B)

-- 再访问表A中不存在的变量a
print(A.a)

    打印结果:99

    查找过程:访问A.a时,表A中没有a这个变量,但是lua发现表A有元表,即表B,于是再到表B中进行查找,但是lua并不是直接在表B中查找变量a,而是调用表B的元方法__index,如果__index为nil,那就会返回nil。如果__index被赋值为一个表(上面的例子就是__index被赋值为表B自己),那么就会到__index指向的那个表(即表B)中进行查询,于是找到了变量a;如果__index被赋值为一个函数,那么查找时就会返回该函数的返回值

    代码如下:

-- 表A
A =
{
}

-- 表B
B =
{
	a = 99
}

-- 给表B的元方法__index进行赋值,这里赋值为一个函数
B.__index = function(table, key)
		print("在元表中访问了变量"..key)
		return 88
	    end

-- 设置表B为表A的元表
setmetatable(A,B)

-- 再访问表A中不存在的变量a
print(A.a)

    打印结果:

    在元表中访问了变量a

    88

    总结元表的查找步骤:

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

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

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


3. 面向对象的封装

-- 类Class的声明,其实就是个table,这里有两个成员变量x,y
Class =
{
	x = 1,
	y = 2
}

-- 设置metatable的元方法__index,指向表Class自己
Class.__index = Class

-- 构造函数,叫什么名字无所谓,这里采用了C++的new名字
function Class:new(x, y)
	print("Class:模拟构造函数new()")

	-- 新建一个对象,这样通过Class:new()函数创建的每一个实例都是独立的
	local tempObj = {}
	tempObj.x = x
	tempObj.y = y

	-- 设置新对象的metatable,谨记:这一步非常重要
	setmetatable(tempObj,Class)

	-- 返回这个新创建的对象
	return tempObj
end

-- 类的其他成员函数1
function Class:Print()
	print("Class:Print()")
	print("x = "..self.x..", y = "..self.y)
end

-- 类的其他成员函数2
function Class:Add(val)
	print("Class:Add()")
	self.x = self.x + val
	self.y = self.y + val
end

-- 类的其他成员函数3
function Class:Modify()
	print("Class:Modify()")
	self.x = 11
	self.y = 22
end

-- 下面是测试代码

-- 新构造一个类实例
local Obj = Class:new(11,22)

-- 调用函数Print()进行打印
Obj:Print()

-- 调用函数Add()进行加操作
Obj:Add(5)

-- 再次调用函数Print()进行打印,会发现调用Add()函数确实成功了
Obj:Print()

-- 做修改
Obj:Modify()

-- 再次调用函数Print()进行打印,会发现调用Modify()函数确实成功了
Obj:Print()

-- 这里打印出Class本身的数据,会发现数据没有改动,说明是新建的类实例互不影响
print("Class Class.x = "..Class.x..", Class.y = "..Class.y)

测试结果如下:


4. 面向对象的继承多态

--------------------------------------   基类Class    ------------------------------------------

-- 类Class的声明,其实就是个table,这里有两个成员变量x,y
Class =
{
	x = 0,
	y = 0
}

-- 设置metatable的元方法__index,指向表Class自己
Class.__index = Class

-- 构造函数,叫什么名字无所谓,这里采用了C++的new名字
function Class:new(x, y)
	print("Class:模拟构造函数")

	-- 新建一个对象,这样通过Class:new()函数创建的每一个实例都是独立的
	local tempObj = {}
	tempObj.x = x
	tempObj.y = y

	-- 设置新对象的metatable,谨记:这一步非常重要
	setmetatable(tempObj,Class)

	-- 返回这个新创建的对象
	return tempObj
end


-- 类的其他成员函数1
function Class:Print()
	print("Class:Print() x = "..self.x..", y = "..self.y)
end

-- 类的其他成员函数2
function Class:Add(val)
	print("Class:Add()")
	self.x = self.x + val
	self.y = self.y + val
end

-- 类的其他成员函数3
function Class:Modify()
	print("Class:Modify()")
	self.x = 111
	self.y = 222
end

--------------------------------------   子类SubClass    ---------------------------------------

-- 子类SubClass的声明,这里又声明了一个新的变量z
SubClass =
{
	z = 0
}

-- 设置元表为Class
setmetatable(SubClass, Class)

-- 设置metatable的元方法__index,指向表SubClass自己
SubClass.__index = SubClass


-- 构造函数
function SubClass:new(x,y,z)

	print("模拟构造函数:SubClass")

	-- 先调用父类的构造函数,构造出一个父类的实例
	local tempObj = Class:new(x,y)

	-- 将该对象的元表指向SubClass,谨记:这步非常重要,一定不要弄错了,是SubClass
	setmetatable(tempObj,SubClass)

	-- 新属性z赋值,有了子类自己的数据,这样就是子类实例了
	tempObj.z = z

	return tempObj
end


-- 定义一个新的成员函数
function SubClass:SubPrint()
	print("SubClass:SubPrint() x = "..self.x..", y = "..self.y..", z = "..self.z)
end

-- 重定义父类的函数Add(),注意:和父类的不同,这里是增加了2倍的val
function SubClass:Add(val)
	print("SubClass:Add()")
	self.x = self.x + 2*val
	self.y = self.y + 2*val
end


-------------------------------------    下面是测试代码      -----------------------------------

-- 构造一个基类实例
local Obj = Class:new(11,22)

-- 调用函数Print()进行打印
Obj:Print()

-- 调用函数Add()进行加操作
Obj:Add(5)

-- 再次调用函数Print()进行打印,会发现调用Add()函数确实成功了
Obj:Print()

-- 做修改
Obj:Modify()

-- 再次调用函数Print()进行打印,会发现调用Modify()函数确实成功了
Obj:Print()

-- 这里打印出Class本身的数据,会发现数据没有改动,说明是新建的类实例互不影响
print("Class Class.x = "..Class.x..", Class.y = "..Class.y)


print("\n")

-- 构造一个子类实例
local SubObj = SubClass:new(1,2,3)

-- 访问父类的函数
SubObj:Print()

-- 访问子类自己的函数
SubObj:SubPrint()

-- 调用Add(),这里会发现实际调用的是子类的Add()函数,即实现了多态
SubObj:Add(5)

-- 再次调用自己的函数,会发现调用自己的Add()函数确实成功了
SubObj:SubPrint()

测试结果如下:



猜你喜欢

转载自blog.csdn.net/yzf279533105/article/details/80099358