lua——元表、元方法、繼承


【元表】

元表中的鍵為事件(event),稱值為元方法(metamethod)。


通過函數getmetatable查詢不論什么值的元表,通過函數setmetatable替換表的元表。

setmetatable(僅僅能用於table)和getmetatable(用於不論什么對象)
語法:setmetatable (table, metatable),對指定table設置metatable      【假設元表(metatable)中存在__metatable鍵值。setmetatable會失敗】
語法:tmeta = getmetatable (tab)。返回對象的元表(metatable)             【假設元表(metatable)中存在__metatable鍵值,當返回__metatable的值】


【元方法】

元表能夠控制對象的數學運算、順序比較、連接、取長、和索引操作的行為。


當Lua對某值運行當中一個操作時。檢查該值是否含有元表以及對應的事件。

假設有,與該鍵關聯的值(元方法)控制Lua怎樣完畢操作。
每一個操作的鍵是由其名字前綴兩個下划線“__”的字符串。比如。操作“加(add)”的鍵是字符串"__add"。




特別一提,要獲取給定對象的元方法。我們使用表達式
metatable(obj)[event]
它應被解讀為
rawget(getmetatable(obj) or {}, event)
就是說,訪問一個元方法不會調用其它元方法,並且訪問沒有元表的對象不會失敗(僅僅是結果為nil)。


"add": + 操作。 
以下的getbinhandler函數定義Lua怎樣選擇二元操作的處理程序。首先嘗試第一操作數,假設它的類型未定義該操作的處理程序。則嘗試第二操作數。
function getbinhandler (op1, op2, event)
 return metatable(op1)[event] or metatable(op2)[event]     
end


事件:
add
sub
mul
div
mod
pow
unm 一元操作-
concat 連接操作..
len 取長操作#
eq 同樣操作==
lt 小於操作<
le
index 索引訪問table[key],參數table,key
newindex 索引賦值table[key] = value, 參數table, key, value
call lua調用


__index元方法:
依照之前的說法,假設A的元表是B,那么假設訪問了一個A中不存在的成員。就會訪問查找B中有沒有這個成員。這個過程大體是這樣,但卻不全然是這樣。實際上,即使將A的元表設置為B。並且B中也確實有這個成員,返回結果仍然會是nil。原因就是B的__index元方法沒有賦值。依照我的理解,__index方法是用來確定一個表在被作為元表時的查找方法。


【用戶數據和元方法】

Userdata:
A userdata offers a row memory area, with no predefined operations in Lua, which we can use to store anything (分配指定數量的內存在棧上。把數據已用戶自己定義的數據結構存放進去)

lua_newuserdata() -- allocates a block of memory with the given size, pushes the corresponding userdatum on the stack, and returns the block address

Metatable:
The usual method to distinguish one type of userdata from other userdata is to create a unique metatable for that type.
Lua code cannot change the metatable of a userdatum, it cannot fake our code (Lua 不能改變userdata里面的metatable,所以userdata的metatable能夠用作唯一標示符來識別userdata,這里metatable拿來推斷是否傳入了正確的userdata參數)

【繼承】

cocos2dx里的繼承:

function class(classname, ...)
    local cls = {__cname = classname}

    local supers = {...}
    for _, super in ipairs(supers) do
        local superType = type(super)
        assert(superType == "nil" or superType == "table" or superType == "function",
            string.format("class() - create class \"%s\" with invalid super class type \"%s\"",
                classname, superType))

        if superType == "function" then
            assert(cls.__create == nil,
                string.format("class() - create class \"%s\" with more than one creating function",
                    classname));
            -- if super is function, set it to __create
            cls.__create = super
        elseif superType == "table" then
            if super[".isclass"] then
                -- super is native class
                assert(cls.__create == nil,
                    string.format("class() - create class \"%s\" with more than one creating function or native class",
                        classname));
                cls.__create = function() return super:create() end
            else
                -- super is pure lua class
                cls.__supers = cls.__supers or {}
                cls.__supers[#cls.__supers + 1] = super
                if not cls.super then
                    -- set first super pure lua class as class.super
                    cls.super = super
                end
            end
        else
            error(string.format("class() - create class \"%s\" with invalid super type",
                        classname), 0)
        end
    end

    cls.__index = cls
    if not cls.__supers or #cls.__supers == 1 then
        setmetatable(cls, {__index = cls.super})
    else
        setmetatable(cls, {__index = function(_, key)
            local supers = cls.__supers
            for i = 1, #supers do
                local super = supers[i]
                if super[key] then return super[key] end
            end
        end})
    end

    if not cls.ctor then
        -- add default constructor
        cls.ctor = function() end
    end
    cls.new = function(...)
        local instance
        if cls.__create then
            instance = cls.__create(...)
        else
            instance = {}
        end
        setmetatableindex(instance, cls)
        instance.class = cls
        instance:ctor(...)
        return instance
    end
    cls.create = function(_, ...)
        return cls.new(...)
    end

    return cls
end
  原理就不細說了。改動__index。使得在訪問其成員的時候能遍歷全部的supers父類去查找該成員(類似js里的原型鏈,但那是一條鏈,這里lua可自由發揮)。

  他的第一個參數是類名,后面的參數能夠是父表或者函數,函數的話僅僅能有一個,是用來作為創建函數的__create,會在.new的時候被調用。

  用法:

local UIScene = class("UIScene")
UIScene.__index = UIScene

function UIScene.extend(target)
    local t = tolua.getpeer(target)
    if not t then
        t = {}
        tolua.setpeer(target, t)
    end
    setmetatable(t, UIScene)
    return target
end

function UIScene.create()
    local scene = cc.Scene:create()
    local layer = UIScene.extend(cc.Layer:create())
    layer:init()
    scene:addChild(layer)
    return scene   
end
  getpeer/setpeer我還不是非常理解,從網上找到說明留着消化:

Those are tolua functions. The tolua manual (for example here) has explanations for them.

tolua.setpeer (object, peer_table) (lua 5.1 only)
Sets the table as the object's peer table (can be nil). The peer table is where all the custom lua fields for the object are stored. When compiled with lua 5.1, tolua++ stores the peer as the object's environment table, and uses uses lua_gettable/settable (instead of lua_rawget/set for lua 5.0) to retrieve and store fields on it. This allows us to implement our own object system on our table (using metatables), and use it as a way to inherit from the userdata object. Consider an alternative to the previous example:
-- a 'LuaWidget' class
LuaWidget = {}
LuaWidget.__index = LuaWidget

function LuaWidget:add_button(caption)
    -- add a button to our widget here. 'self' will be the userdata Widget
end

local w = Widget()
local t = {}
setmetatable(t, LuaWidget) -- make 't' an instance of LuaWidget

tolua.setpeer(w, t) -- make 't' the peer table of 'w'

set_parent(w) -- we use 'w' as the object now

w:show() -- a method from 'Widget'
w:add_button("Quit") -- a method from LuaWidget (but we still use 'w' to call it)
When indexing our object, the peer table (if present) will be consulted first, so we don't need to implement our own __index metamethod to call the C++ functions.
tolua.getpeer (object) (lua 5.1 only)
Retrieves the peer table from the object (can be nil).




免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM