lua面向对象封装及元表(metatable)性能测试_lua 元表 性能-程序员宅基地

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

local test = {}

function test:get_x()
    return self.x or 0
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( 999 )
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 0
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( 999 )
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 = 10000000

f_tm_start()
local cnt = 0
for i = 1,ts do
    cnt = test( cnt,1 )
end
f_tm_stop( "call function native" )

f_tm_start()
local cnt = 0
for i = 1,ts do
    cnt = tb.test( cnt,1 )
end
f_tm_stop( "call function as table value" )

f_tm_start()
for i = 1,ts do
    cnt = empty_mt6.test( cnt,1 )
end
f_tm_stop( "call function with 3 level metatable" )

f_tm_start()
for i = 1,ts do
    cnt = mt_tb.test( cnt,1 )
end
f_tm_stop( "call function with 10 level metatable" )
复制代码

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

1
2
3
4
5
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重继承的话,几乎慢了一倍。

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

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/three_bird/article/details/51286046

智能推荐

如何使用MP4SetTrackESConfiguration_mp4 esconfiguration-程序员宅基地

在使用MP4v2制作.mp4档案时,如果你要使用的Audio编码格式是AAC,那么你就需要使用MP4SetTrackESConfiguration这个函式来设定解码需要的资料。在网路上看到的例子都是以FAAC编码为居多,大多都可以参考需要的设定,设定MP4SetTrackESConfiguration的方式,都是先利用FAAC里的 faacEncGetDecoderSpecificInfo得到想_mp4 esconfiguration

如何让你的代码维护性提高-程序员宅基地

可维护性解释这本书解释了可维护软件中的“维护”的意思:可维护性是软件质量的一个标准,代表一个系统可被修改的难易程度。所以它是面向程序员的,假设两个软件完成相同的功能,但一个软件的源码,让其他人或者一段时间之后的自己,很难理解,更不用提修改了,就说明这个软件的可维护性比另一个差。软件维护有4种方式:发现并纠正bug(纠正性维护);适应操作系统或运行环境的改变(适应性维护);根据需求增加新的功能(

Ibatis读写CLOB数据-程序员宅基地

Ibatis是一个高效,方便,易于学习的数据访问组件,在性能上比hibernate高,学习难度也比hibernate和jdo要低,而且它比直接使用jdbc方便和易于维护。所以Ibatis深入大家的喜爱,一些对性能有更高的要求的系统(如保险,金融行业系统),或改造遗留系统时,Ibatis是数据访问组件的首选。 在使用Oracle数据库时,读取CLOB和BLOB等大类型的数据一直是个比较犯难的事,

golang:字符串分割_golang 分割字符串_天已青色等烟雨来的博客-程序员宅基地

strings.Split 支持单个分隔符strings.FieldsFunc 支持多个分隔符func main() { srcStr1 := "abc:def:k:g" desStr1 := strings.Split(srcStr1, ":") fmt.Printf("ret:%s\n", desStr1) srcStr2 := "a b c,def,k,g" desStr2 := strings.FieldsFunc(srcStr2, splitFunc) fmt.Printf(_golang 分割字符串

《数据结构》-第七章 查找(知识点总结)_数据结构查找知识点总结_代码太难敲啊喂的博客-程序员宅基地

第七章 查找本章开始介绍关于前几章这些数据结构的相应的运算—查找。关于查找的不同算法为每年考试考查的重点,主要分为线性结构的查找、树形结构的查找、散列结构的查找及字符串模式匹配,同时分析各个查找方法的查找效率。其中线性结构的查找(静态查找表)主要代表为顺序查找、分块查找和折半查找;树形结构的查找(动态查找表)为二叉排序树、二叉平衡树、B树和B+树;B树和B+树作为本章的难点。对于B树,要求掌握插入、删除和查找的操作过程:对于B+树,仅要求了解其基本概念和性质。散列结构主要以散列表(哈希表)为代表,应_数据结构查找知识点总结

NAT端口复用型-程序员宅基地

实验进出接口调用就不删了,因为模拟器上进接口调用命令执行时间较长。不通看看服务器到出口路由器通不通看看出口路由器到外网是不是通都通,那看看是否进行了nat地址转换说明没有nat转换表项show running-config立马就通了看转发表已经有了下面是测试,去除出接口调用..._nat端口复用

随便推点

pip安装openpyxl_澾慟的博客-程序员宅基地

好的。要用pip安装openpyxl,可以在命令行中运行以下命令:pipinstall openpyxl在运行这条命令之前,你需要确保已经安装了pip。如果还没有安装pip,可以在命令行中运行以下命令进行安装:python -m ensurepip --default-pip如果你使用的是Python 3,那么上述命令就应该改为:python3 -m ensurepip --def..._pip安装openpyxl

Ubuntu 使用 ./ 没有文件或目录_ubuntu找不到文件或目录-程序员宅基地

在ubuntu 64位下运行 ./xxx 会跳出来说没有这个文件或者目录,但是ls看又有这个文件,很是奇怪。其实原因很简单,是因为他没有32位的运行库 ia32-libs ,直接安装即可。apt-get install ia32-libs可能会出现以下情况:XXX被其他软件包引用了,然而下列软件包会取代它:lib32ncurses5 lib32z1,那么可以使用如下方法:方法一:s..._ubuntu找不到文件或目录

Python函数随机种子random_state-程序员宅基地

目录1、划分训练集和测试集的类train_test_split2、构建决策树的函数3、构建随机森林random_state是一个随机种子,是在任意带有随机性的类或函数里作为参数来控制随机模式。当random_state取某一个值时,也就确定了一种规则。random_state可以用于很多函数,比较常见的是用于以下三个地方:1、划分训练集和测试集的类train_test_sp...

学习solr过程中的问题org.apache.solr.client.solrj.beans.BindingException: class: class-程序员宅基地

org.apache.solr.client.solrj.beans.BindingException: class: class使用实体类javabean对象添加索引 但是没有在实体对象上添加注解修改前 private String id; public String getId() { return id; } public void setId(String id) { th...

TTEFS - 基于文件过滤驱动的透明加密内核_半透明加密原理-程序员宅基地

更多产品信息: http://www.byte2code.com1产品综述1.1产品简介TTEFS(True Transparent Encryption File System)是一款文件透明加密开发包。基于LayerFsd模型设计,采用文件系统过滤驱动技术实现。产品的目标是使客户可快速和稳定开发基于TTEFS的文档安全管理系统、文档防泄密系统、文档权限管理系统、文档标密..._半透明加密原理

Centos7下安装redis-6.0.1_redis-6.0.1.tar.gz 安装-程序员宅基地

1.下载redis安装包wget http://download.redis.io/releases/redis-6.0.1tar.gz2.解压tar -zxvf redis-6.0.1.tar.gz3.安装依赖yum install gcc4.到redis目录cd redis-6.0.15.编译安装make PREFIX=/usr/local/redis install6.遇到的错误报错解决如下:由于未安装gcc导致的,执行如下命令yum install gcc_redis-6.0.1.tar.gz 安装