Lua 支持虚函数的解决方案
概述
lua本身没有提供类似C++虚函数机制,调用的父类方法调用虚函数可能会出现问题。
问题分析
分析这段代码和输出
local Gun = {}
-- 示例,实际应用还要考虑构造,虚表等情况
function LuaClass(Class, Parent)
setmetatable(Class, {__index = Parent})
Class._Super = Parent
end
function Gun:Attack()
print("开始攻击");
self:Load()
self:Fire()
end
function Gun:Load()
print("装弹");
end
function Gun:Fire()
print("开枪");
end
Gun:Attack();
local Cannon = {}
LuaClass(Cannon, Gun)
function Cannon:Attack()
print("大炮开始攻击")
self._Super:Attack()
end
function Cannon:Fire()
print("开炮")
end
print("-------------------------------------")
Cannon:Attack()
输出:

红线圈出的地方虚函数调用错误,应该打印"开炮"。
使用元表来面向对象时,要注意__index元方法的语义:
当你通过键来访问 table 的时候,如果这个键没有值,那么Lua就会寻找该table的metatable(假定有metatable)中的__index 键。如果__index包含一个表格,Lua会在表格中查找相应的键
如果__index包含一个函数的话,Lua就会调用那个函数,table和键会作为参数传递给函数。
__index 元方法查看表中元素是否存在,如果不存在,返回结果为 nil;如果存在则由 __index 返回结果
可知__index只是提供一种递归的查询方式,其中并未包含虚函数的调用机制。
而
Gun:Attack() 等价于 Gun.Attack(self)
self._Super:Attack() 等价于 Gun.(Gun) 注意self._Super = Gun
所以调用父类Attack函数中,self的语义是Gun这张表,后面调用的就一直是Gun方法,所以最好调用的是Gun的Fire,而不是Cannon的Fire。
解决方案
使用指针指向调用函数的表,在调用父类的方法时,使父类的self的语义是调用者。
注意这种实现和C++的虚函数调用思路是不一样的,细节请参考我的另一篇文章:
跳转链接:c++虚函数表、多态
替换问题分析中的LuaClass方法
function LuaClass(Class, Parent)
local FindVal = function(InClass, Key)
local Raw = rawget(InClass, Key)
if nil ~= Raw then
return Raw, InClass
end
if nil ~= InClass.__Base then
return FindVal(InClass.__Base, Key)
end
end
Class.__Base = Parent
Class.__ClassPtr = Class
local Index = function(_, Key)
local Val, ClassPtr = FindVal(Parent, Key)
if nil == Val then
return
end
Class.__ClassPtr = ClassPtr
return Val
end
setmetatable(Class, {__index = Index})
local SuperIndex = function(_, Key)
return function(_, ...)
local OriClassPtr = Class.__ClassPtr
if nil == OriClassPtr.__Base then
return
end
local Val, ClassPtr = FindVal(OriClassPtr.__Base, Key)
if nil == Val then
return
end
Class.__ClassPtr = ClassPtr
local Ret = {Val(Class, ...)}
Class.__ClassPtr = OriClassPtr
return table.unpack(Ret)
end
end
Class._Super = setmetatable({}, {__index = SuperIndex})
end
输出:

- 在__index元方法查询的时候,标记当前调用方法所在的表。
- 在_Super的元表__index元方法查询的时候,找到标记表的方法,使用Class表作为第一个参数self传入。
备注
- 支持虚函数有性能开销,可以在LuaClass加个参数控制是否支持虚函数。
Lua 支持虚函数的解决方案的更多相关文章
- LUA使用虚函数与使用回调函数
------------------虚函数overload-------------------------- --回调:寻路中格子坐标改变 CHero.OnSearchToCellsChange = ...
- C++中的虚函数总结
一.什么是虚函数.纯虚函数.抽象基类 虚函数:在某基类中声明为 virtual 并在一个或多个派生类中被重新定 义的成员函数. 纯虚函数:是一种特殊的虚函数,使用virtual关键字,并且在其后面加上 ...
- Lua中如何实现类似gdb的断点调试—07支持通过函数名称添加断点
我们之前已经支持了通过函数来添加断点,并且已经支持了行号的检查和自动修正.但是通过函数来添加断点有一些限制,如果在当前的位置无法访问目标函数,那我们就无法对其添加断点. 于是,本篇我们将扩展断点设置的 ...
- Lua 字符串查找函数 string.find(s, pattern [, init [, plain]] )【转】
函数原型 string.find(s, pattern [, init [, plain]] ) s: 源字符串 pattern: 待搜索模式串 init: 可选, 起始位置 plain: 我没用过 ...
- C++虚函数和函数指针一起使用
C++虚函数和函数指针一起使用,写起来有点麻烦. 下面贴出一份示例代码,可作参考.(需要支持C++11编译) #include <stdio.h> #include <list> ...
- EC笔记,第二部分:9.不在构造、析构函数中调用虚函数
9.不在构造.析构函数中调用虚函数 1.在构造函数和析构函数中调用虚函数会产生什么结果呢? #; } 上述程序会产生什么样的输出呢? 你一定会以为会输出: cls2 make cls2 delete ...
- C++之虚函数和多态
干货较多-需要自己深思理解: C++支持两种多态性: 1.编译时多态性(静态绑定-早绑定) 在程序编译阶段即可以确定下来的多态性 通过使用 重载机制(重载函数)实现 (模板)http://blog.c ...
- windows 下 gvim/vim lua支持问题,neocomplete等插件支持
此文是按照知乎 https://www.zhihu.com/question/29333426 "windows下vim的lua支持问题?" 一文汇总的解决方案. 题主提供了不错的 ...
- C++学习基础十二——纯虚函数与抽象类
一.C++中纯虚函数与抽象类: 1.含有一个或多个纯虚函数的类成为抽象类,注意此处是纯虚函数,而不是虚函数. 2.如果一个子类继承抽象类,则必须实现父类中的纯虚函数,否则该类也为抽象类. 3.如果一个 ...
随机推荐
- 使用vue实现排序算法演示动画
缘起 最近做的一个小需求涉及到排序,界面如下所示: 因为项目是使用vue的,所以实现方式很简单,视图部分不用管,本质上就是操作数组,代码如下: { // 上移 moveUp (i) { // 把位置i ...
- 浪姐打分看不够?用几行Python代码模拟评委打分
大家好鸭~我是小熊猫比赛大家都看过吧,每次是不是都对比赛成绩充满期待.特别是浪姐的打分看的简直欲罢不能- 今天就用Python来模拟评委打分,这个案例很短也很简单,很适合新手跟小白练习. 在某次十佳歌 ...
- POI导出复杂Excel,合并单元格(1)
/** * 导出复杂excel 合并单元格 (HSSFWorkbook) */ @GetMapping("/testHSSFWorkbook.do") public void te ...
- 机器学习-K近邻(KNN)算法详解
一.KNN算法描述 KNN(K Near Neighbor):找到k个最近的邻居,即每个样本都可以用它最接近的这k个邻居中所占数量最多的类别来代表.KNN算法属于有监督学习方式的分类算法,所谓K近 ...
- labview从入门到出家7(进阶篇)--队列的使用
本节简单讲解队列在Labview中的使用,队列你可以认为就是一组先进先出的数据列表,在Labview中常用来缓存和传递数据.用了这么久的队列,个人认为有个方便的地方在于数据传递的把控,不管是局部变量还 ...
- 手机APP无法抓包HTTPS解决方案
问题表现:某个APP的HTTPS流量抓取不到,Fiddler报错,但可以正常抓取其它的HTTPS流量 可能原因: 1.Flutter应用,解决方案:https://www.cnblogs.com/lu ...
- 自定义nginx的日志格式存储到Filebeat和Logstash
vim /etc/nginx/nginx.conf log_format main '$remote_addr - $remote_user [$time_local] ' '"$reque ...
- 或许是 WebGIS 下一代的数据规范 - OGC API 系列
目录 1. 前言 1.1. 经典的 OGC 标准回顾 1.2. 共同特点与时代变化 1.3. 免责声明 2. 什么是 OGC API 2.1. OGC API 是一个开放.动态的规范族 2.2. OG ...
- elastsearch整合springboot
文档地址: https://www.baeldung.com/elasticsearch-java
- 服务器配置IP
1.服务器系统一般有两个或多个网卡.在企业中一般给服务器网卡配一个可连外网的IP,如172.16.20.22 255.255.255.0 172.16.20.1 方便联网下载安装部分软件,若没有VPN ...