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

转载自:https://www.2cto.com/kf/201805/744307.html

简介

1lua面向对象编程是基于元表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

举例说明:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

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的一套查找机制,也是凭借这个机制,才能够实现面向对象的

举例说明:

?

1

2

3

4

5

6

test =

{

}

-- 访问表test中不存在的变量a

print(test.a)

打印结果:nil

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

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

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

举例说明:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

-- 表A

A =

{

}

-- 表B

B =

{

    a = 99

}

-- 设置表B为表A的元表

setmetatable(A,B)

-- 再访问表A中不存在的变量a

print(A.a)

打印结果依然为:nil

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

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

代码如下:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

-- 表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被赋值为一个函数,那么查找时就会返回该函数的返回值

代码如下:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

-- 表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. 面向对象的封装

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

-- 类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.面向对象的继承多态

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

133

134

--------------------------------------   基类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/guominyou/article/details/88691382