Class Lua,用哪种方式进行类表和实例化?

Class Lua,用哪种方式进行类表和实例化?,class,lua,instantiation,lua-table,metatable,Class,Lua,Instantiation,Lua Table,Metatable,这个问题源于 本教程包括以下代码: Dog = {dog1 = 'original dog class'} function Dog.new(self, ... ) newObj = {sound = 'woof'} self.__index = self return setmetatable(newObj, self) end function Dog.makeSound(self, ... ) print('I say' .. self.sound) en

这个问题源于 本教程包括以下代码:

Dog = {dog1 = 'original dog class'}
function Dog.new(self, ... )
    newObj = {sound = 'woof'}
    self.__index = self
    return setmetatable(newObj, self)
end

function Dog.makeSound(self, ... )
    print('I say' .. self.sound)
end

print('Dog=', Dog)
print('Dog.metatable=', getmetatable(Dog))  -- this will output nothing

myDog = Dog.new(Dog)
print('\nmyDog=', myDog)
print('myDog.metatable=', getmetatable(myDog))
myDog.makeSound(myDog)
这是教程中上述代码的结果:

wirelessprvnat-172-17-106-141:Programming frankhe$ th test2.lua
Dog=  {
  makeSound : function: 0x0a6cec20
  dog1 : "original dog class"
  new : function: 0x0a6cec00
}
Dog.metatable=  nil 

myDog=  {
  sound : "woof"
}
myDog.metatable=  {
  makeSound : function: 0x0a6cec20
  __index : 
    {
      makeSound : function: 0x0a6cec20
      __index : 
        {
          makeSound : function: 0x0a6cec20
          __index : 
            {
              makeSound : function: 0x0a6cec20
              __index : 
                {
                  makeSound : function: 0x0a6cec20
                  __index : {...}
                  dog1 : "original dog class"
                  new : function: 0x0a6cec00
                }
              dog1 : "original dog class"
              new : function: 0x0a6cec00
            }
          dog1 : "original dog class"
          new : function: 0x0a6cec00
        }
      dog1 : "original dog class"
      new : function: 0x0a6cec00
    }
  dog1 : "original dog class"
  new : function: 0x0a6cec00
}
I saywoof

虽然教程中的实现成功地打印了“我说胡说”,但myDog的元表显然不像我们预期的那样令人满意。因此,我的解决方案如下(区别在于Dog.new):

我的解决方案的结果是:

wirelessprvnat-172-17-106-141:Programming frankhe$ th test2.lua
Dog=  {
  makeSound : function: 0x0d7f2978
  dog1 : "original dog class"
  new : function: 0x0d7f2958
}
Dog.metatable=  nil 

myDog=  {
  sound : "woof"
}
myDog.metatable=  {
  __index : 
    {
      makeSound : function: 0x0d7f2978
      dog1 : "original dog class"
      new : function: 0x0d7f2958
    }
}
I saywoof
我的代码打印“我说胡说”,并且有一个更精确的表结构。 我想知道哪个实现是正确的,教程中的那个还是我的?
此外,我想知道为什么教程中的代码会生成Dog的元表的迭代定义。

让我们看看table
Dog
的结构,在构建Dog对象之后,它有一个
\uU索引
元方法集,如下所示:

Dog = {}            --> table: 000000000079a510     
Dog.__index = Dog   --> table: 000000000079a510
打印
Dog
表时,键
\u index
具有其包含表的值,从而导致递归。标准的Lua不能很好地打印表,因此这个
print
函数必须在大约5个级别之后停止(即:
\u index:{…}
停止递归)。正如@siffiejoe在评论中提到的,这是一种将单个表用于对象方法和元方法的技术

关于哪些实施是正确的;在Lua中创建对象的方法有很多。示例类虽然没有错,但IMHO没有不必要地使用全局变量。它的实现通过
Dog
newObj
泄漏到全球环境中。这不是一个孤立的问题,但当一个更大的程序的一部分,这可能是一个难以找到的错误源。另一种技术是将类作为模块实现。对实现使用局部变量,并仅导出实例化新对象所需的内容

例如,让我们看看
Dog
类的重构:

-- class: dog.lua
--
local Dog = {}     -- the objects implementation
Dog.__index = Dog  -- also, its own metatable

-- instantiate a Dog object:
local function new(name, sound)
    local self = {
        name = name,
        sound = sound or 'woof'
    }
    return setmetatable(self, Dog)
end

-- implement object methods:
function Dog.say(self)
    print(('<%s> says: %s'):format(self.name, self.sound))
end

-- implement object metamethods (unique to Dog objects):
function Dog.__tostring(self)
    return ('Dog: %s'):format(self.name)
end

-- module exports:
return {
    new = new;       -- Dog constructor
    __object = Dog;  -- Dog object table/metatable
}
可以通过链接元表并在
new
函数中调用父构造函数来处理继承:

-- class: colorfuldog.lua
--
local Dog = require 'dog'   -- import the parent class

local ColorfulDog = setmetatable({}, Dog.__object)  -- inherit from Dog
ColorfulDog.__index = ColorfulDog                   -- also, its own metatable

-- instantiate a new ColorfulDog object:
local function new(name, sound, color)
    local self = Dog.new(name, sound)  -- construct the parent first
    self.color = color
    return setmetatable(self, ColorfulDog)
end

-- implement or override object methods:
function ColorfulDog.lookat(self)
    print(('<%s> looks: %s'):format(self.name, self.color))
end

-- implement object metamethods (unique to ColorfulDog objects):
function ColorfulDog.__tostring(self)
    return ('ColorfulDog: %s'):format(self.name)
end

-- module exports
return {
    new = new;
    __object = ColorfulDog;
}
运行上述输出:

$lua test.lua
说:汪
说:arf!
说:拉夫
长相:棕色
狗:罗孚狗:马克斯彩色狗:莱西

正如我前面所说,在Lua中创建类有很多种方法,这只是一个例子。无论您选择如何实现对象,保持全局环境清洁仍然是一个很好的做法。

是什么
print
函数产生了该输出?我不相信这是正确的层次结构。我相信每次调用
Dog:new()
时,您的更改只会创建一个新表,而不是重用现有的
Dog
表。(尽管我同意
new
函数中的
self.\uu index=self
很奇怪,因为每个类只需要做一次(而不是每个实例做一次,因为
self
是类)。我添加了一张照片来更恰当地描述这个问题。
Print
只是lua中的默认打印功能。这两种方法都是正确的,但都不太理想。教程中的方法使用相同的方法和元方法表来节省内存。请参阅的第一部分。您的方法为每个Dog obj分配一个新的元表诸如此类。你应该在
Dog.new
之外分配元表并重新使用它。Stock lua
print
不会那样打印表。啊,
th
似乎是带有定制
print
功能的torch解释器。我相信它只是被表本身作为
\uu index
和rec弄糊涂了最大深度为4。如果你调用
setprintlevel
更改该值,我打赌你会看到它打印的嵌套级别与你设置的打印级别完全相同。嗨,亚当,非常感谢你提供了这个非常详细的答案!我还有一个问题,我想你很可能能够回答我的问题。所以请回答我的问题好的,当你有空的时候,谢谢你!谢谢你为这个简洁的例子介绍lua中的类和继承!
-- original example:
myDog = Dog.new(Dog)  --> must pass in the global Dog table to create new objects

-- vs --

-- refactored example:
local Dog = require 'dog'   --> Dog object factory
local myDog = Dog.new()     --> instantiate new Dog
-- class: colorfuldog.lua
--
local Dog = require 'dog'   -- import the parent class

local ColorfulDog = setmetatable({}, Dog.__object)  -- inherit from Dog
ColorfulDog.__index = ColorfulDog                   -- also, its own metatable

-- instantiate a new ColorfulDog object:
local function new(name, sound, color)
    local self = Dog.new(name, sound)  -- construct the parent first
    self.color = color
    return setmetatable(self, ColorfulDog)
end

-- implement or override object methods:
function ColorfulDog.lookat(self)
    print(('<%s> looks: %s'):format(self.name, self.color))
end

-- implement object metamethods (unique to ColorfulDog objects):
function ColorfulDog.__tostring(self)
    return ('ColorfulDog: %s'):format(self.name)
end

-- module exports
return {
    new = new;
    __object = ColorfulDog;
}
-- script: test.lua
--
local Dog = require 'dog'
local ColorfulDog = require 'colorfuldog'

local d1 = Dog.new 'Rover'
local d2 = Dog.new('Max', 'arf!')
local d3 = ColorfulDog.new('Lassie', 'ruff', 'brown')

d1:say()  -- sugar for d1.say(d1)
d2:say()
d3:say()  -- inherited from Dog
d3:lookat()

print(d1, d2, d3)