第一节 前言
通过上一章的阅读,你应该知道Lpc的函数的基本工作方式。你应该学
会如何声明和调用函数。更进一步,你应该有能力自己写函数定义,即
使你是首次接触Lpc。你知道了函数调用是如何执行的,函数必须有一个
返回值,除非是 void 类型的。
下面我们来看一个例子。看看巫师的工作室,可能几乎都有类似下面
的一段代码。(这个不同的 MudLib 是不一样的)
-----
void create();
inherit "/std/room";
void create()
{
room::create();
set( "short", "巫师工作室");
set( "long", "这是一片未曾开垦的土地,需要巫师的画笔去描绘。\n" );
set( "exits", ([
"trill" : "/u/trill/workroom",
]);
create_door("south", "时空之门", "north", 0);
setup();
}
-----
如果你理解了前面的几章,你应该能立刻指出几点:
1) create() 是一个函数的定义部分。"void create();" 是它的函数
声明,却有在 create() 里面有个 room::create() 没有声明。
2) 它调用了set(), create_door(), setup(),在代码中却没有声明。
3) 在第三行,既不是变量声明也不是函数声明,更不是函数定义。
这一章就是回答以下问题的:
1) room::create()是什么东西?
2) 函数 set(),create_door(),在哪里声明、哪里定义?
3) 第三行是什么东西?
第二节 面对对象编程(OOP)的继承
继承是面对对象编程的一个基本特性。它使你能写一些基本的代码能
以不同的方式被不同的程序使用。一个 MudLib 做的就是创建一些基本
的 Object ,在此基础上你能用他们创建自己特殊的 Object。
如果不得不写那些定义巫师工作室所必须的所有代码,你可能要写超
过 1000 行的代码,这样你才可能有这个 room 所需要的所有函数。如果
每个 room 都必须这么做的话,首先是浪费硬盘空间;其次,你写的代
码可能根本无法和别人的写的代码组合在一起,每个人对 room 扩展的
特性的各种函数搞出每个人自己的标准。比如,你可能把 room 的长描
述的接口函数叫做 GetLong(),别的巫师可能喜欢把它命名为 query_long()。
这个就是各个 MudLib 不兼容的根本问题,因为它们 Object 相互继承、
调用上使用不同的协议。
OOP 能克服上面的问题。在上面的例子中,你直接使用的函数都定义
在继承的叫做 "/std/room.c" 的 Object 里面了。它有所有那些 room 常
用的函数的定义。当你写创建一个特别的 room,你通过继承得到 room
的的基本的功能,通过加入你自己写的函数,这样就把它变成了一个独
特的 room 了。
OOP 通过继承不只是提高了代码的可重用性,而且它可以是你只关心
继承基本的 Object 后,那些特性需要改变的,而不用考虑那些东西是
需要的,就是说当你看到别人写好的 Object 时,发现已经很象你心中
所想的东西,就只有一点点不符合,那你所要做只是继承原先的 Object,
然后加入和想的那一点点不同的东西就可以了。
OOP 当然不只是继承,还有别的一些。但是对于我们现在是谈论继承
的最基本的概念。
第三节 继承是如何工作的?
还记得最开始提的三个问题吗?在这一节里面,将解答这些问题。
首先最后一个问题:
例子中的第三行:
-----
inherit "/std/room";
-----
是做什么?
我想到了现在,你应该你猜出来这一行确保你写的巫师工作室继承了
标准的 room: "/std/room.c" 的特性。通过继承获得标准 room 的功能,
你就可以使用那些已经在 "/std/room.c" 底下声明和定义好的所有函数。
这就是为什么你可以直接使用 set(), create_door() 这些函数的原因了。
在你自己写的函数 create(),你只要设置那些你要的值就可以了。这些
值使得你的 room 与众不同,但确保和别的 Object 能相互作用。
那 room::create() 是做什么的?
你可能看到类似的东西比如: ::create()、::init()等。可能现在完全
理解有些困难。你只要有个印象就可以了。
在你继承的那些 Object 中,可能会有 create() 函数,但是在你写的
的 Object 中也有 create() 了,那么 Driver 将只调用你写 create() 来
完成一些初始化工作,一般情况下这是你所希望的。可是,有时候,你
发现你继承的 Object,比如 /std/room.c 中的 create(),也是有用的,
那怎办?那么你可以这么做:
在你写的 room 的 create() 当中加入
-----------------
room::create();
-----------------
这一行,"::"这个操作符加在一个函数 example() 的前面,那么它就调
用它继承的 Object 中的那个函数,如果 "::"的前面没有任何限定,那
么就调用所有继承的当前的一级的 Object 中叫做 example() 的函数,
如果有限定,那么就只调用给定的 Object 的叫做 example() 的函数,
在我们的例子就调用 /std/room.c 的 create() 函数。
"::"这个操作符,是用于调用继承的 Object 一个特殊的函数。通常
把 "::" 操作符叫做 域访问操作符 。
一个对比例子:
-----
#1
inherit "/std/room";
void create()
{
create();
}
-----
-----
#2
inherit "/std/room";
void create()
{
room::create();
}
-----
例子 1 的后果是灾难性的。当它被装入内存中,Driver 调用 create(),
接着 create() 调用 create(),create() 还接着调用 create(),如此往
复永无穷尽。就是说,create() 持续不断的调用自己,除非 Driver 发
现这是一个太深的递归,将其强制退出。
例子 2 是对的,但是除了浪费内存之外没有任何作用,因为它和 room.c
的功能没有区别。对于这个 Object,Driver 调用它的 create(),在它的
create() 通过 room::create() 返回去调用 room.c 中的 create()。
总的来说,每个 MudLib 都是不同的,每个自己有一套标准函数的集
合,它们对于一些基本的功能都能完成。有些功能特性多有些,有些少
一些,有些在一些特殊方面完成特殊的工作,比如 Es2 带了对中文的处
理。但是如果这个 MudLib 如果比较完善的话,它一般要提供一些标准
的 Object 的特性和功能的说明。一般可能在 "/doc/build" 底下,可能
叫做 "room_prop"、"monster_prop" 等。看看这些,你会得到你继承的
Object 的有的函数,它们的输入参数,返回的数据类型,以及能完成
什么工作。
第四节 小结
实际上复杂对象的继承是很复杂的,就这么简单解释是远远不够的。
这篇简介只是在你创建一些简单的 Object 能使用继承。更全面的讨论
我们将在 Lpc进阶 里面看到。
现在你应该知道:
1) 每个 MudLib 有自己的一套基本 Object 的库,有着一些基本的
功能,巫师们通过继承能更容易创建自己的 Object,不同的
Object 之间的交互作用也更容易一些。
2) 每个 MudLib 有自己一套标准。通常有自己一些文档说明标准的
Object 是什么,有什么样的功能。
3) 你可以用这样继承别的 Object 的功能:
-----
inherit "filename";
-----
这里的 filename 是被继承的 Object 的文件名。inherit "xxx"
要在你的写的代码的开头部分。
尊重作者 转载请注明出处52mud.com