                   第四章 Lpc的继承简介 
 
                        第一节 前言 
  通过上一章的阅读，你应该知道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" 
        要在你的写的代码的开头部分。


