Lua本身是没有面向对象支持的,但面向对象编程在逻辑复杂的大型工程却很有用。于是很多人用Lua本身的数据结构table来模拟面向对象。最简单的一种方法是把对象的方法、成员都放到table中。如:

-- file:test.lua

local test = {}

function test:get_x()
return self.x or
end function test:set_x( _x )
self.x = _x
end local test_module = {} function test_module.new()
local t = {}
for k,v in pairs( test ) do
t[k] = v
end return t
end return test_module

调用也比较简单:

-- file:main.lua

local test = require "test"

local _t = test.new()

_t:set_x(  )
print( _t:get_x() )

这已经很像面向对象编程。但我们可以看到这样写有些缺点:

1.数据和方法混在一起(当然这不是什么大问题,C++也是这样)

2.每创建一个对象,都要将方法复制一遍

3.没法继承

Lua有强大的元表(metatable),利用它我们可以更优雅地封装一下:

1.先统一封装一个面向对象函数:

-- file:oo.lua

local oo = {}

local cls = {}

local function new( clz )
local t = {}
setmetatable(t, clz) return t
end function oo.class( parent,name )
local t = {}
cls[name] = t parent = parent or {}
rawset( t,"__index",t )
setmetatable( t,{ __index = parent,__call = new } ) return t
end return oo

2.然后重新写类的实现:

-- file:test.lua

local oo = require "oo"

local test = oo.class( nil,... )

function test:get_x()
return self.x or
end function test:set_x( _x )
self.x = _x
end return test

3.调用也更加简单了:

-- file:main.lua

local Test = require "test"

local _t = Test()

_t:set_x(  )
print( _t:get_x() )

可以看到,利用元表,我们可以把方法全部放到元表中,与对象成员数据分开。元表本身是一个表,它也有元表,可以把父类作为元表的元表实现继承。我们如果再扩展一下,还可以实现对象方法的热更,对象统计...

  虽然元表很巧妙,但它的实现是有代价的。Lua得先在table中查找是否有相同的值,如果没有,再去元表找。如果是多重继承,那么还得一层层元表找下去。下面我们来测试一下元表的效率。

-- lua metatable performance test
-- 2016-04-01
-- xzc local test = function( a,b ) return a+b end local empty_mt1 = {}
local empty_mt2 = {}
local empty_mt3 = {}
local empty_mt4 = {}
local empty_mt5 = {}
local empty_mt6 = {}
local empty_mt7 = {}
local empty_mt8 = {} local mt = {}
mt.test = test local mt_tb = {} setmetatable( empty_mt8,{__index = mt} )
setmetatable( empty_mt7,{__index = empty_mt8} )
setmetatable( empty_mt6,{__index = empty_mt7} )
setmetatable( empty_mt5,{__index = empty_mt6} )
setmetatable( empty_mt4,{__index = empty_mt5} )
setmetatable( empty_mt3,{__index = empty_mt4} )
setmetatable( empty_mt2,{__index = empty_mt3} )
setmetatable( empty_mt1,{__index = empty_mt2} )
setmetatable( mt_tb,{__index = empty_mt1} ) local tb = {}
tb.test = test local ts = f_tm_start()
local cnt =
for i = ,ts do
cnt = test( cnt, )
end
f_tm_stop( "call function native" ) f_tm_start()
local cnt =
for i = ,ts do
cnt = tb.test( cnt, )
end
f_tm_stop( "call function as table value" ) f_tm_start()
for i = ,ts do
cnt = empty_mt6.test( cnt, )
end
f_tm_stop( "call function with 3 level metatable" ) f_tm_start()
for i = ,ts do
cnt = mt_tb.test( cnt, )
end
f_tm_stop( "call function with 10 level metatable" )

在我的笔记本上测试,结果为:

local ts = 10000000
call function native 1091772 microsecond
call function as table value 1287172 microsecond
call function with 3 level metatable 2014431 microsecond
call function with 10 level metatable 3707181 microsecond

可以看到,采用第一种方法封闭的面向对象比原生函数调用慢不了多少,但用第二种方法实现3重继承的话,几乎慢了一倍。

  在实际项目中,我们用的是第二种封装方式,最主要是可以继承和热更代码。虽然效率有一定影响,但实际应用中逻辑消耗的时间比函数调用的时间仍大得多,这点损耗可以接受。这个世界上没有最快,只有更快,不必盯着程序的效率看。在满足项目要求的情况下,开发效率也是很值得考虑的。

lua面向对象封装及元表(metatable)性能测试的更多相关文章

  1. lua面向对象封装

    lua面向对象的一个封装,直接贴代码 --swfclass = {};local cs = {};function _class( child, base, ... )--    _.s( child ...

  2. Lua面向对象 --- 封装

    工程结构: Player.lua: Player = {} function Player:new() local self = {} setmetatable(self, {__index = Pl ...

  3. Lua的元表(metatable)

    metatable允许我们改变table的行为 > Lua中的每一个表都可以有metatable(后面我们将看到userdata也有Metatable) > Lua默认创建一个不带meta ...

  4. lua元表(metatable)和元方法(metamethod)

    (一) 元表概念: 引言:Lua中的每个值都有一套预定义的操作集合,如数字相加等.但无法将两个table相加,此时可通过元表修改一个值的行为,使其在面对一个非预定义的操作时执行一个指定操作. 访问机制 ...

  5. lua元表Metatable

    Lua 中的每个值都可以用一个 metatable. 这个 metatable 就是一个原始的 Lua table , 它用来定义原始值在特定操作下的行为. 你可以通过在 metatable 中的特定 ...

  6. Lua中的元表(metatable)、元方法(metamethod)详解

    在第一次看见这两样东西的时候,可能会觉得它很深奥,但其实很好理解,虽然实际上它可能真的很深奥.(小若:停!滚粗.) 1.知道为什么1 + 1 = 2吗? 为什么在Lua中,1+1会等于2呢?(小若:难 ...

  7. Step By Step(Lua面向对象)

    Step By Step(Lua面向对象) Lua中的table就是一种对象,但是如果直接使用仍然会存在大量的问题,见如下代码: 1 Account = {balance = 0}2 function ...

  8. Lua面向对象

    lua中的table就是一种对象,但是如果直接使用仍然会存在大量的问题,如下: 1 Account = {balance = 0}2 function Account.withdraw(v)3 Acc ...

  9. Lua面向对象之三:其它一些尝试

    1.尝试一:子类对象调用被覆盖了的父类函数 根据元表设置流程,我们只有将父类元表找到就能调用父类的方法了 ①在子类Circle中增加一个调用父类方法的函数 --调用父类被子类覆盖了的name方法 fu ...

随机推荐

  1. Java基础知识强化74:正则表达式之分割功能 (扩展练习)

    1. 看程序写结果:(面试题考过) package cn.itcast_03; /* * 分割功能练习 */ public class RegexDemo2 { public static void ...

  2. Can't connect to local MySQL server through socket '/tmp/mysql.sock' (111)

    遇着这个提示的话,如果本地只安装了一个mysql,这里写的很详细的 http://www.blogjava.net/asenyifei/articles/82575.html 看这里可以解决,如果本地 ...

  3. ListControl一细节处理

    做VC ListControl的时候发现一个问题,右击菜单的时候,在没有列表数据的空白处也会出现菜单,这可够纠结的,于是用了点方法解决了,希望分享同样不爽的朋友们.代码如下: void CXXXDlg ...

  4. Mysql group_concat

    select p.id,p.parent_id,group_concat(distinct(CONCAT("分类名称:",c.name)) order by c.id desc s ...

  5. asp.net微信开发第七篇----高级群发(图文)

    上一篇介绍了如何群发文本消息,本篇将介绍如何群发图文信息,上传图文信息所需的素材,界面如下: 我们先看从素材库中获取图文素材的代码,界面: 素材列表,我是使用的repeater控件, 前台代码如下: ...

  6. Repeater的ItemDataBound 事件中e.Item.DataItem 的数据类型

    1.使用DataSet和DataTable绑定数据源时 DataRowView view = (DataRowView)e.Item.DataItem; 2.DataReader绑定数据源时 Syst ...

  7. php访问方法外变量

    class Capture { private static $_CapSite = 222; function dd() { echo self::$_CapSite; } } $cc=new Ca ...

  8. 启动mySQL安装出现1067错误

    可能几种的办法: 删除data目录下的ib_logfile0和ib_logfile1 查看my.ini文件中的dir设置 查看err文件,如果是temp出现错误文件,则添加temp文件的路径

  9. 1.1HTML的基本概念

    1.WWW有3个基本的组成部分,分别是URL(统一资源定位器),HTTP(超文本传输协议),HTML(本文本标记语言). 2.一个HTML文件是由一系列的元素和标签组成的,元素不区分大小写. 3.&l ...

  10. php 求两个文件的相对路径

    网上看了一些这个题的一些解答方法,不过大多数就是对目前需求而定的,比如 $a = '/a/b/c/d/e.php'; $b = '/a/d/12/34/c.php'; getpath($a , $b ...