【问题标题】:How to stop Lua object constructors from sharing nested tables between instances?如何阻止 Lua 对象构造函数在实例之间共享嵌套表?
【发布时间】:2020-03-24 15:51:05
【问题描述】:

谁能修复我有问题的 Lua 对象系统?

  • 不同的实例有不同的数值字段
  • 但是 .. 当我将表添加到我的初始化字段时,这些表会在不同实例之间共享(参见下面的示例)。

我认为我需要对初始字段进行深层复制,但我看不到在哪里。我的代码如下。有什么建议吗?

Object = {}

function Object:new (o)
  -- o = deep_copy(o) or {} -- <== this didn't work
  -- self = deep copy(self) -- <== this didn't work
  o = o or {}  
  setmetatable(o, self)
  self.__index = self
  self.__tostring = show
  return o
end

Account = Object:new{balance = 0,all={}}

function Account:push(v)
  self.all[#self.all+1] = v
end

function Account:deposit(v)
  self.balance = self.balance + v end

function Account:withdraw (v)
  if v > self.balance then
    error"insufficient funds" end
  self.balance = self.balance - v
end

function show(i,  str,sep)
  str,sep = "{",""
  for k,v in pairs(i) do
    if type(v) ~= "function" then
      str = str..sep..tostring(k)..":"..tostring(v)
      sep = ", "
    end
  end
  return str .. "}"
end

为了说明问题,下面我有两个实例ab

当我更新a,b 的数字字段时,不同的实例会得到不同的值。

但是当我在一个实例a 中更新all 表时,它会在另一个实例b 中更改它。

a=Account:new()
b=Account:new()
a:deposit(100)
b:deposit(200)
b:push(10)
b:push(20)
a:push(300)

print("a all", show(a), show(a.all))
print("b all", show(b), show(b.all))

输出应该是:

a all   {balance:100}   {3:300}
b all   {balance:200}   {1:10, 2:20}

但实际出来的是:

a all   {balance:100}   {1:10, 2:20, 3:300}
b all   {balance:200}   {1:10, 2:20, 3:300}

【问题讨论】:

  • o = deep_copy(o) or {} 看起来像您需要的。什么“没用”?
  • 是的,这就是难题。在gist.github.com/timm/… 我做了你建议的改变。但错误仍然存​​在(写入一个实例内的列表会影响另一个)。所以我检查了我的deep copy 函数,运行了一些测试,这些部分似乎工作正常(嵌套列表被复制)。这意味着谜题仍然存在......
  • 我在问你:当你这样尝试时,发生了什么以及你观察到什么导致你得出结论认为它“不起作用”?
  • 感谢您的提问。请参阅 gist.github.com/timm/… 处的 bug.md 文件
  • 请不要在其他人回复后以显着改变其含义的方式编辑 cmets。

标签: object constructor lua


【解决方案1】:

您使用self.__index = self,并且您的原型有一个all 字段,但您从未在您创建的对象中设置一个。因此,对其的访问(就像push 所做的那样)将始终通过__index 并最终到达原型。

【讨论】:

  • 感谢您为此所做的工作。如果我注释掉-- self.__index = self,则会导致错误attempt to call a nil value (method 'new')。所以需要这条线来加入类名。
  • 我的意思不是你不需要那个;就是每个对象都需要自己的all
【解决方案2】:

正如约瑟夫所说,您需要为每个对象创建不同的all
您可以编写函数Account:new() 或编写接受初始化程序的Object:new() 函数(这样您就不需要为每个类实现不同的:new()):

Object = {__init = function(o) end}

function Object:new(o, init)
  -- when creating an instance:
  --  o: the object itself
  -- when creating a class:
  --  o:    table containing shared fields and methods
  --  init: initializer for instances of this class
  o = o or {}
  self.__init(o)
  setmetatable(o, self)
  self.__index = self
  if init then
    function o.__init(o)
      self.__init(o)
      init(o)
    end
  end
  return o
end

Account = Object:new({}, function(o) o.all={}; o.balance=0; end)

function Account:push(v)
  self.all[#self.all+1] = v
end

function Account:deposit(v)
  self.balance = self.balance + v
end

function Account:withdraw (v)
  if v > self.balance then
    error"insufficient funds" end
  self.balance = self.balance - v
end


function show(i,  str,sep)
  str,sep = "{",""
  for k,v in pairs(i) do
    if type(v) ~= "function" then
      str = str..sep..tostring(k)..":"..tostring(v)
      sep = ", "
    end
  end
  return str .. "}"
end

Object.__tostring = show


a=Account:new()
b=Account:new()
a:deposit(100)
b:deposit(200)
b:push(10)
b:push(20)
a:push(300)

print("a all", show(a), show(a.all))
print("b all", show(b), show(b.all))

【讨论】:

    【解决方案3】:

    如果您想从 Object 继承,最简单的方法是也为帐户创建一个新的构造函数。这应该为每个对象设置平衡,而不是在类本身上。

    Object = {}
    Account = {}
    
    function Object:new(o)
        o = o or {}
        setmetatable(o, self)
        self.__index = self
        self.__tostring = show
        return o
    end
    
    function Account:new(o)
        o = o or Object:new(o)
        setmetatable(o, self)
        self.__index = self
        o.balance = 0
        o.all = {}
        return o
    end
    
    a all   {all:table: 0x55ab93ec70d0, balance:100}        {1:300}
    b all   {all:table: 0x55ab93ec6ed0, balance:200}        {1:10, 2:20}
    

    【讨论】:

      猜你喜欢
      • 2014-07-03
      • 2014-01-21
      • 2021-01-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-07-16
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多