Lua对象表在对象实例之间泄漏,但不会泄漏记录。

我正在学习 Lua,所以希望这是一个容易回答的问题。以下代码无法正常工作。变量 childContext 在所有类的实例之间泄漏。

--[[--
Context class.
--]]--
CxBR_Context = {}

-- 名称上下文
CxBR_Context.name = "未设置名称的上下文"

-- 子上下文列表
CxBR_Context.childContexts = {}

-- 创建上下文类的新实例
function CxBR_Context:New (object)
  object = object or {} -- 如果用户没有提供对象,则创建对象
  setmetatable(object, self)
  self.__index = self
  return object
end

-- 添加子上下文
function CxBR_Context:AddChildContext(context)
  table.insert(self.childContexts, context)
  print("在 " .. self.name .. " 中添加子上下文 " .. context.name)
end

--[[--
Context 1 class. Inherits CxBR_Context
--]]--
Context1 = CxBR_Context:New{name = "Context1"}

--[[--
Context 1A class. Inherits CxBR_Context
--]]--
Context1A = CxBR_Context:New{name = "Context1A"}

--[[--
Context 2 class. Inherits CxBR_Context
--]]--
Context2 = CxBR_Context:New{name = "Context2"}

--[[--
测试
--]]--
context1  = Context1:New() -- 创建上下文1类的实例
print(context1.name .." 有 " .. table.getn(context1.childContexts) .. " 个子节点")
context2  = Context2:New() -- 创建上下文2类的实例
print(context2.name .." 有 " .. table.getn(context2.childContexts) .. " 个子节点")
context1A = Context1A:New() -- 创建上下文1A类的实例
print(context1A.name .." 有 " .. table.getn(context1A.childContexts) .. " 个子节点")

context1:AddChildContext(context1A) -- 将上下文1A添加为上下文1的子级

print(context1.name .." 有 " .. table.getn(context1.childContexts) .. " 个子节点") -- 结果正确,有一个子节点
print(context2.name .." 有 " .. table.getn(context2.childContexts) .. " 个子节点") -- 为什么返回1,应该是0

查看 Object oriented lua classes leaking 我可以通过将构造函数更改为以下内容来解决问题:

-- 子上下文列表
-- CxBR_Context.childContexts = {}

-- 创建上下文类的新实例
function CxBR_Context:New (object)
  object = object or { childContexts = {} } -- 如果用户没有提供对象,则创建对象
  setmetatable(object, self)
  self.__index = self
  return object
end

所以我的问题是:

  1. 是否有更清晰的方式来声明类变量,类似于第一个示例,以便不必在构造函数中包含它们?
  2. 为什么 CxBR_Context.name 起作用,而表 CxBR_Context.childContexts 不起作用?
点赞
用户1847592
用户1847592

1.

使用table.insert(self.member, v)会修改指向self.member的容器的内容,这个容器恰好是最近的超类中存在该成员的成员。

如果您需要为子类的成员赋值,请明确地执行此操作。使用以下代码:

-- create a copy of array with one additional element
self.childContexts = { context, unpack(self.childContexts) }

代替

table.insert(self.childContexts, context)

2.

因为您使用了赋值来为CxBR_Context.name赋值,而没有使用它来为CxBR_Context.childContexts赋值。

2013-03-27 07:47:59
用户2198692
用户2198692
  1. 不,我不这么认为。你希望每个创建的 Child Context 对象都有它自己的 childContexts 字段。

  2. 它工作是因为你将一个包含 name 字段的 table 传递给了 New 方法。你所做的是:

     Context1 = CxBR_Context:New{name = "Context1"}
    
    • 创建一个字段为 "name" 值为 "Context1" 的 table。
    • 将该 table 传递给构造函数。
    • 在构造函数中,将元表(原 CxBR_Context table)分配给第一步创建的 table。

    因此,当你调用 Context1.name 时,你从调用构造函数时创建的 Context1 table 检索出一个字段。但是,当你使用 childContext 索引它而没有这样的字段存在(因为在构造函数阶段你只创建了一个 "name" 字段),Lua 在 __index table 中查找,该 table 是 CxBR_Context 公用的,适用于所有 ContextX 对象。

编辑:

object = object or { childContexts = {} } -- create object if user does not provide one

事实上,这也不起作用,如果你像这样提供你自己的 table:

Context1 = CxBR_Context:New{name = "Context1"}

只有当你的 object 参数为 nil 时,即当你只用 self 参数调用构造函数时,它才起作用。

2013-03-27 07:48:04