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方法。