Lua面向对象实现

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/zhaixh_89/article/details/84347596

lua中没有类的概念,一般所说的类其实就是一个table实现的。关于lua类有两种实现方式
第一种是在cocos2d-x引擎目录下图示文件

function clone(object)
    local lookup_table = {}
    local function _copy(object)
        if type(object) ~= "table" then
            return object
        elseif lookup_table[object] then--查询是否已复制过该对象
            return lookup_table[object]
        end
        local new_table = {}
        lookup_table[object] = new_table
        for key, value in pairs(object) do
            new_table[_copy(key)] = _copy(value)--递归复制
        end
        --同时复制lua元表
        return setmetatable(new_table, getmetatable(object))
    end
    return _copy(object)
end

--Create an class.
function class(classname, super)
    local superType = type(super)
    local cls
    --如果父类既不是函数也不是table则说明父类为空
    if superType ~= "function" and superType ~= "table" then
        superType = nil
        super = nil
    end

    --如果父类的类型是函数或者是C对象
    if superType == "function" or (super and super.__ctype == 1) then
        -- inherited from native C++ Object
        cls = {}

        --如果父类是表则复制成员并且设置这个类的继承信息
        --如果是函数类型则设置构造方法并且设置ctor函数
        if superType == "table" then
            -- copy fields from super
            for k,v in pairs(super) do cls[k] = v end
            cls.__create = super.__create
            cls.super    = super
        else
            cls.__create = super
        end
        --设置类型的名称
        cls.ctor    = function() end
        cls.__cname = classname
        cls.__ctype = 1

        --定义该类型创建实例的函数为基类的构造函数后复制到子类实例
        --并且调用子数的ctor方法
        function cls.new(...)
            local instance = cls.__create(...)--调用基类构造函数
            -- copy fields from class to native object
            for k,v in pairs(cls) do instance[k] = v end
            instance.class = cls
            instance:ctor(...)
            return instance
        end

    else
        --如果是继承自普通的lua表,则设置一下原型,并且构造实例后也会调用ctor方法
        -- inherited from Lua Object
        if super then
            cls = clone(super)
            cls.super = super
        else
            cls = {ctor = function() end}
        end

        cls.__cname = classname
        cls.__ctype = 2 -- lua 1表示继承自C++对象;2表示继承自lua表对象
        cls.__index = cls

        function cls.new(...)
            local instance = setmetatable({}, cls)
            instance.class = cls
            instance:ctor(...)
            return instance
        end
    end

    return cls
end

通过阅读上述代码可以看出,如果是继承自c++,则无需我们手动调用基类构造函数,如果是继承Lua表,则需要手动调用构造函数,例如:

/定义名为 Shape 的基础类  
local Shape = class("Shape")  
  
  
//ctor() 是类的构造函数,在调用 Shape.new() 创建 Shape 对象实例时会自动执行  
function Shape:ctor(shapeName)  
    self.shapeName = shapeName  
    printf("Shape:ctor(%s)", self.shapeName)  
end  
  
//为 Shape 定义个名为 draw() 的方法  
function Shape:draw()  
    printf("draw %s", self.shapeName)  
end  
  
// Circle 是 Shape 的继承类  
local Circle = class("Circle", Shape)  
  
  
function Circle:ctor()  
    -- 如果继承类覆盖了 ctor() 构造函数,那么必须手动调用父类构造函数  
    -- 类名.super 可以访问指定类的父类  
    Circle.super.ctor(self, "circle")  
    self.radius = 100  
end  
  
  
function Circle:setRadius(radius)  
    self.radius = radius  
end  
  
  
// 覆盖父类的同名方法  
function Circle:draw()  
    printf("draw %s, raidus = %0.2f", self.shapeName, self.raidus)  
end  

可以看到Circle类的构造函数里调用了基类的构造函数,但是这样使用显得代码不美观,也不方便使用,有没有更好的使用办法呢?答案是有的,这里我结合网上众多资料以及个人理解做一下总结:

第一种方案:

我们可以将调用基类的函数封装出来,如下:


function super(o,...)
    --if (o and o.super and o.super.ctor) then
        o.super.ctor(o,...)
    --end
end

这样在调用的时候直接传进来参数就可以了,不过还是要手动调用

第二种方案:

结合class类的实现,我们在创建类的时候可以这样写

class函数第二个参数我们直接传入一个函数,按照程序原意中继承c++的流程执行,最后也能实现

扫描二维码关注公众号,回复: 4465459 查看本文章

第三种方案:

反复斟酌class类实现,可以发现,当我们从c++继承时能够自动调用构造函数,当从lua继承时能不能也自动实行构造函数呢,经过摸索,发现利用元表确实可以实现,以下是我的做法

function class(classname, super)
    local superType = type(super)
    local cls
    if superType ~= "function" and superType ~= "table" then
        superType = nil
        super = nil
    end

    if superType == "function" or (super and super.__ctype == 1) then
        print("--inherited from native C++ Object or create function:" .. classname)
        -- inherited from native C++ Object
        cls = {}

        if superType == "table" then
            -- copy fields from super
            for k, v in pairs(super) do
                cls[k] = v
            end
            cls.__create = super.__create
            cls.super = super
        else
            cls.__create = super
        end

        cls.ctor = function()
        end
        cls.__cname = classname
        cls.__ctype = 1

        function cls.new(...)
            local instance = cls.__create(...)
            -- copy fields from class to native object
            for k, v in pairs(cls) do
                instance[k] = v
            end
            instance.class = cls
            instance:ctor(...)
            return instance
        end
    else
        print("-- inherited from Lua Object:" .. classname)
        -- inherited from Lua Object
        if super then
            cls = clone(super)
            cls.super = super
        else
            cls = { ctor = function()end }
        end

        cls.__cname = classname
        cls.__ctype = 2 -- lua
        cls.__index = cls

        function cls.new(...)
            local instance = {}
            if(cls.super) then
                instance = cls.super:new(...)
            end
            setmetatable(instance, cls)
            for k, v in pairs(cls) do
                instance[k] = v
            end
            instance.class = cls
            instance:ctor(...)
            return instance
        end
    end
    _G[classname] = cls;
    package.loaded[classname] = cls
    setmetatable(cls, {__index = _G});
    setfenv(1, cls)
    return cls
end

做法也是比较简单。创建实例的时候判断有没有基类,有的话调用构造函数,并设置基类对象元表为子类对象即可完美实现,之后我们创建类时可以直接这样传入基类名字即可

第二种类的实现方式是从网上看到的一位大神写的,代码如下:

这种从概念上实现面向对象的方法做到以下几点:

(1)有类定义和对象的概念,类定义通过new来创建对象,并且同时调用自己的构造函数

(2)子类可以访问基类的成员函数

(3)类定义不能够调用函数(除了new之外),只有对象才能调用函数

(4)构造函数调用有和c++一样的层级关系,先调用父类的构造函数,再调用子类的构造函数

--lua面向对象:概念意义上的实现
local _class={}
 
function class(super)
    local class_type={}
    --注意:因为重载了__newindex函数, 所以ctor不要定义为nil
    class_type.ctor=false
    class_type.super=super
    class_type.new=function(...) 
            local obj={}
            --下面的块只做了一件事:依次从父类到当前子类调用构造函数ctor
            do
                local create
                create = function(c,...)
                    if c.super then
                        create(c.super,...)
                    end
                    if c.ctor then
                        c.ctor(obj,...)
                    end
                end
 
                create(class_type,...)
            end
            setmetatable(obj,{ __index=_class[class_type] })
            return obj
        end
    
    --新加成员:防止定义类调用函数
    local vtbl={}
    _class[class_type]=vtbl
 
    setmetatable(class_type,{__newindex=
        function(t,k,v)
            vtbl[k]=v
        end
    })
 
    --只有定义类修改了__newindex
    --vbtl只属于定义类
    --new出来的对象共享所有定义类的引用,但独享自己新增加的成员变量
    if super then
        setmetatable(vtbl,{__index=
            function(t,k)
                local ret=_class[super][k]
                vtbl[k]=ret
                return ret
            end
        })
    end
 
    return class_type
end

使用方法;

base_type=class()		-- 定义一个基类 base_type
 
function base_type:ctor(x)	-- 定义 base_type 的构造函数
	print("base_type ctor")
	self.x=x
end
 
function base_type:print_x()	-- 定义一个成员函数 base_type:print_x
	print(self.x)
end
 
function base_type:hello()	-- 定义另一个成员函数 base_type:hello
	print("hello base_type")
end


----以上是基本的 class 定义的语法,完全兼容 lua 的编程习惯。我增加了一个叫做 ctor 的词,作为构----造函数的名字。
----下面看看怎样继承:

test=class(base_type)	-- 定义一个类 test 继承于 base_type
 
function test:ctor()	-- 定义 test 的构造函数
	print("test ctor")
end
 
function test:hello()	-- 重载 base_type:hello 为 test:hello
	print("hello test")
end


--现在可以试一下了:

a=test.new(1)	-- 输出两行,base_type ctor 和 test ctor 。这个对象被正确的构造了。
a:print_x()	-- 输出 1 ,这个是基类 base_type 中的成员函数。
a:hello()	-- 输出 hello test ,这个函数被重载了

说明几点:

(1)只有定义类修改了__newindex
(2)vbtl只属于定义类
(3)new出来的对象共享所有定义类的引用,但独享自己新增加的成员变量

有点:

(1)概念上更加清晰,熟悉c++面向对象的很容易了解这个继承的关系

(2)写法上感觉很牛逼,做到了定义不能调用函数这一点

(3)共享函数引用

缺点:

(1)概念上清晰的成本是要更多的时间去理解

(2)虽然做到了c++类定义和对象上的概念区别,但是还是有多东西没有实现

(3)对象也可以定义自己的函数,这一点就直接打破了区分定义和对象的本源,但是价值还是有的

(4)所有new出来对象共享类定义的引用对象,包括不需要复用的函数和table。由此多个对象共享一个定义的table很是个问题!

* 针对(4),可以通过实现定义的init函数,在init函数给不同的对象初始化不同的数据,即使是table!

从实现上可以看到,在new一个对象的时候,没有返回之前是没有设置元表的,因此做到了类定义不能调用函数,不过这种写法有利有弊吧,如果单纯的实现一个功能的时候还是很不错的做法,程序耦合度会很低,这里给出我的使用例子,可以帮助理解

base_type=class()		-- 定义一个基类 base_type
 
function base_type:ctor(x)	-- 定义 base_type 的构造函数
	print("base_type ctor")
	self.x=x
end

function base_type:print_x()	-- 定义一个成员函数 base_type:print_x
	print(self.x)
	self.hello()
end

function base_type:init()
    print('==================')    
end
 
function base_type:hello()	-- 定义另一个成员函数 base_type:hello
	print("hello base_type")
end

输出如下:

k name = print_x
k name = init
k name = hello
base_type ctor
==================

相信都能看懂!

参考:

http://www.cnblogs.com/pk-run/p/4248095.html

猜你喜欢

转载自blog.csdn.net/zhaixh_89/article/details/84347596