Lua 中的面向对象编程

这篇博客探讨了Lua中的面向对象编程,如何通过`self`来模拟C++中的`this`指针,并介绍了利用元表实现类的继承。文章提到了数据与方法的分离,以及通过对偶表方式实现数据和方法的私有化及继承。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

面向对象编程中, 很重要的概念之一是隐含的 “this” 指针, 即有一个变量指代了自身.在 lua 中, 使用 “self” 代表 “this”.

--[[ (1)
基本代码.
--]]
Account = {balance = 0}
function Account.withdraw(v)
    Account.balance = Account.balance - v
end

--[[ (2)
使用 (1) 中的 Account.
调用 a.withdraw(100.0) 时, 函数内部应当调用 a.balance = a.balance - 100.0,
而函数定义为 Account.balance = Account.balance - v, 所以出错.
--]]
Account.withdraw(100.0)
print(Account.balance)           --> -100

a = Account
a.withdraw(100.0)                --> error

--[[ (3)
对 (1) 的改进, 即重写 withdraw() 函数.
这里明确指明了 self 参数, 即调用者.
--]]
Account = {balance = 0}
function Account.withdraw(self, v)
    self.balance = self.balance -v
end

--[[ (4)
使用 (3) 中代码.
--]]
a = Account
a.withdraw(a, 100.0)
print(a.balance)                 --> -100
print

--[[ (5)
在面各对象编程中, "self" 一般是隐含的, 在 Lua 中要隐含 self 指针, 需要另一个':' 语法来定义表中的函数.
--]]
Account = {balance = 0}
function Account:withdraw(v)
    self.balance = self.balance -v   -- 函数内部可直接使用 self, 表示调用者
end

a = Account
a.withdraw(100.0)
print(a.balance)                 --> -100

类的定义:

以 C++ 为例. 在工程代码中, 一个类通常定义一个文件中, 数据是私有的,
函数是公共的. 对数据的操作都通过成员函数进行. 在 Lua 中,
将一个类定义在一个文件中, 可以认为 Lua 的类就是一个模块. 下面代码模拟这种 C++
类的定义, 但这种实现无法实现 “继承”.

-- 文件 (模块) 名: account.lua

function new_account(initial_balance)
    -- 这里的 self, 可以随便命名. 导出的函数都是操作这个表
    local self = {balance = 0}

    -- 赋初值
    self.balance = initial_balance

    -- 可以定义不被外部调用的私有函数
    local function inter_call()
    end

    -- 设置余额
    local function set_balance(balance)
        self.balance = balance
    end

    -- 获取余额
    local function get_balance()
        return self.balance
    end

    -- 取钱
    local function withdraw(v)
        self.balance = self.balance - v
    end

    -- 存钱
    local function deposit(v)
        self.balance = self.balance + v
    end

    ------ 返回可以被外部调用的函数---------------
    return{
        set_balance = set_balance,
        get_balance = get_balance,
        withdraw    = withdraw,
        deposit     = deposit
    }
end

调用 “account.lua” 的代码

require "account"

local my_account = new_account(100)
print(my_account.get_balance())    --> 100

my_account.set_balance(200)
print(my_account.get_balance())    --> 200

my_account.withdraw(50)
print(my_account.get_balance())    --> 150

my_account.deposit(100)
print(my_account.get_balance())    --> 250

由上述代码可知, 它仅仅是利用 Lambda 的特性模拟了 “类” 的实现.
其中一个本质性特点是, 数据与方法分离.

利用元表, 可以实现 “类” 的 “继承”.

-- 基类 -----------------
Account = {balance = 0}

-- 必须调用此函数得到一个对象, 才能保证对象是从 Account 继承而来
function Account:new_class()
    local o = {}
    self.__index = self
    setmetatable(o, self)

    return o
end

--
function Account:get_balance()
    return self.balance
end

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

--
function Account:withdraw(v)
    self.balance = self.balance -v
end

-- 派生类 ---------------------------
SpecialAccount = Account:new_class()

-- 扩充子类
SpecialAccount.user = "anonymous"

function SpecialAccount:set_user(user)
    self.user = user
end

function SpecialAccount:get_user()
    return self.user
end



-- 使用 -----------------------------
my_account = SpecialAccount:new_class()
print(my_account:get_user())         --> anonymous

my_account:set_user("dzf")
print(my_account:get_user())         --> dzf

-- 操作父类的方法
my_account:depoist(500)
print(my_account:get_balance())      --> 500

上述代码中, “类” 的成员变量是公有的, 方法也是私有的.
其中一个本质性特点是数据与方法不分离.

若要将上述两者结合起来, 即实现数据为私有, 方法为私有,
同时数据和方法都可以被继承, 可以使用对偶表的方式实现.
其核心是表的每个域都用一个表来实现.

-- 基类 ------------------------------------------------------------------
Account = {}

-- 账户拥有两个属性: 用户及余额. 用表来实现这两个属性. 事实上, 所有的账户,
-- 基类及子类所有的用户和余额字段都存在同一个表中.
-- 这些域为 local, 外部不可访问, 从而实现数据的私有性.
local balance_field = {}
balance_field[Account] = 0
local user_field = {}
user_field[Account] = "anonymous"

-- 由于数据已经私有, 那么方法就可以定义为 "类" 的方法
function Account:set_user(user)
    user_field[self] = user
end

function Account:get_user()
    return user_field[self]
end

function Account:get_balance()
    return balance_field[self]
end

function Account:witdraw(v)
    balance_field[self] = balance_field[self] - v
end

function Account:deposit(v)
    balance_field[self] = balance_field[self] + v
end

function Account:new()
    local o = {}
    setmetatable(o, self)
    self.__index = self
    self.__mode = "k"          -- 一定要让键为弱引用, 否则不会被回收

    -- 初值
    balance_field[o] = 0
    user_field[o] = "anonymous"

    return o
end

-- 派生类 -------------------------------------------
SpecialAccount = Account:new()

--派生类扩展的域
local site_field = {}
site_field[SpecialAccount] = "none"

function SpecialAccount:get_site()
    return  site_field[self]
end

function SpecialAccount:set_site(site)
    site_field[self] = site
end

-- 测试代码 ----------------------------------------
local myAccount = SpecialAccount:new()
m = myAccount

print(SpecialAccount:get_site())  --> none

print(m:get_balance())            --> 0
m:deposit(500)
print(m:get_balance())            --> 500

print(m:get_site())               --> nil
m:set_site("beijing")
print(m:get_site())               --> beijing
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值