微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

Lua面向对象

Metatable的介绍:

Lua的table可以模拟面向对象,都得益于Metatable的强大之处。在一个table中,如果索引一个元素未能找到,解释器会去该table下的Metatable中的__index元素中去寻找,即table.Metatable.__index.xxx中寻找。

但是如果要修改table中的元素或赋值操作,但是该元素不存在,那么会在table中创建该元素,而不会去Metatable.__index中寻找。

类创建方法的实现方式:

--function.lua

--clone function
function Clone(object)
    local lookup_table = {}--复制元素查找表,为了防止递归无限复制元素
    local function _copy(object)
        if type(object) ~= "table" then --不是table元素就直接返回
            return object
        elseif lookup_table[object] then --是table但是已经在表里了,代表该table已经复制过了;即该table内如果有元素指向本身时候,复制该元素时就会查到表中有本身的记录,则直接把本体的table引用返回。
            return lookup_table[object] 
        end --如果是table并且没有复制过的table,则深度复制table中的数据,并把此table记录在表中,防止再次复制
        local new_table = {}
        lookup_table[object] = new_table --记录时如果Object是元素或函数,则下标是元素名字,如果是table,则下标是table的地址,所以不同层同名table也不会判定重复复制。
        for key,value in pairs(object) do
            new_table[_copy(key)] = _copy(value)
        end
        return new_table
    end
    return _copy(object)
end

--class create
class = function (classname,super)
    local cls
    -- inherited from Lua Object
    if super then
        cls = Clone(super)
        cls.super = super
        if not cls.Ctor then
            cls.Ctor = function() end
        end
    else
        cls = {Ctor = function() end}
    end

    cls.__cname = classname
    cls.__ctype = 2 -- lua
    cls.__index = cls
    
    function cls.New(...)
        local instance = setMetatable({},cls)
        instance:Ctor(...)
        return instance
    end
    return cls
end

分析:

1.class方法中两个参数,classname代表的是要创建的类的类名,super代表所创建类的基类。首先class判断是否有supper,如果没有基类,会为本类创建空的构造方法。如果有基类,则调用Clone方法来克隆基类,这里Clone方法会把基类的成员函数映射到派生类里面去。

2.New方法实现:这里会把构造出来的类的实例instance的元表设定成类table自身,并且由于类table自身的__index参数还是类table自身。instance中没有任何成员函数,用instance调用成员函数的时候,本是找不到函数的,但是因为instance里面的元表(Metatable)存在即指挥类自身,并且类自身里面还有__index。所以还会去类自身table中的__index中搜索,此时__index还是指向自身,所以自身的成员方法就能找到了。

3.类自身只放成员方法,不能放成员变量,原因如下:

   a.由于Metatable的原理,即只有读的时候会去Metatable中找,写的时候如果instance中没有,不会去索引Metatable而是在instance中新增一个

   b.由于继承时候,Ctor构造方法的直接调用者是instance,所以各级构造方法构造的变量都会放到instance中,而非类自身table里。

Lua多实例(一个类可以获取多个类的对象)基类实现:

--object.lua
local Object = class("Object")
return Object

使用方法
--multi.lua
local Object = require "object"
local Multi = class("Multi",Object)

function Multi:Ctor(...)
    --super constructor
    Object.Ctor(self)
    --class memebers constructor
    self.m_mem = 0
end
function Multi:Show()
    print(self.m_mem)
end
local instance1 = Multi:New()
instance1:Show()
local instance2 = Multi:New()
instance2:Show()

第一步:用class方法创建multi类继承于Object类。

第二步:派生类multi中的构造方法是Object.Ctor的引用,所以要重新写派生类multi的构造方法。multi类Ctor构造multi的数据时候,首先要在multi中构造父类的数据,所以要在派生类的Ctor中先调用父类的Ctor。这里必须使用Object.Ctor(self),不能使用Object:Ctor。原理是这样的:调用起源是New方法中instance:Ctor,Object.Ctor(self)是在instance:Ctor中调用的,所以这里的self则是instance。如果写成Object:Ctor,则以后的Ctor参数则变成Object,这样父级的构造方法构造的数据就放不到instance中了。总体来说由于调用Ctor的调用者是instance,所以之后派生链上的所有Ctor方法的参数都是instance,即派生类的实例。

第三步:在super.Ctor调用之后构造派生类的成员变量,即self.m_mem = 0这一行。

第四步:定义multi类的方法Show,注意所有成员方法中的self也是instance,因为外部调用成员方法是用instance:func()的。

总结:类中的所有成员方法包括Ctor方法普通方法调用者都是instance,即类的对象。self自然也是instance而非multi自身。


Lua单实例类(即一个类只能初始化一个对象)的实现:

--singleton.lua
local Object  = require "object"
local Singleton = class("Singleton",Object)

function Singleton:GetSingleton(...)
    if self._instance == nil then
	self._instance = self.New(...)
    end 
    return self._instance	
end 

return Singleton    	

分析:此处Singleton为单实例的最高基类,该类中添加的GetSingleton方法的实质作用是在所有以Singleton为基类的方法中都用该方法获取实例(第一次获取则初始化实例),而不用New方法。此方法调用者是Singleton或派生类本身,所以传入的self也是Singleton或派生类本体,这时要在类本体中添加一个_instance的变量,并调用New来获取一个实例放在此变量中,这便是唯一的实例。

使用方法
--single.lua
local Singleton = require "singleton"
local Single = class("Single",Singleton)

function Single:Ctor(...)
    --super constructor
    Singleton.Ctor(self)
    --class memebers constructor
    self.m_mem = 0
end
function Single:Show()
    print(self.m_mem)
end
local instance = Single:GetSingleton()
instance:Show()

派生类的重写构造方法是一样的,初始化实例的时候要用GetSingleton方法而非New方法

图像解析两种基类的内存表示:

         

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐