你現在應該了解函式基本的功能. 你應該可以宣告並呼叫一個函式. 另外, 你應該能認識函式定義, 雖然你可能是第一次接觸 LPC. 你現在並不見得能定義你自己的函式. 函式是 LPC 物件的基石. 函式中的程式碼, 要別的函式呼叫它們的時候才會執行. 呼叫一個函式時, 作出呼叫的函式要給它輸入值, 才能執行被呼叫的函式. 被呼叫的函式執行其程式碼, 並傳回某種資料型態的傳回值給呼叫它的函式. 沒有傳回值的函式屬於無傳回值 (void) 型態.仔細看過你自己的工作室程式碼之後, 它看起來大概像這樣 (視 mudlib 而定):
-----
inherit "/std/room";-----void create() {
::create();
set_property("light", 2);
set_property("indoors", 1);
set("short", "Descartes 的工作室");
set("long", "此處是 Descartes 工作的地方.\n這裡是一個立方體.\n");
set_exits( ({ "/d/standard/square" }), ({ "square" }) );
}如果你到目前為止, 所有的課本內容都了解的話, 你應該能認出以下的程式碼:
這一章會找出這些問題的解答, 你現在應該腦中應該有這些問題:
- 1) create() 是函式的定義. (嘿 ! 他沒有宣告它)
- 2) 它呼叫 set_property() 、set()、set_exits(), 沒有一個函式在這段程式碼中曾有宣告或定義.
- 3) 最上面有一行, 不是宣告變數或函式, 也不是函式定義 !
- 1) 為什麼沒有宣告 create() ?
- 2) 為什麼 set_property() 、set() 、set_exits() 已經宣告並定義過了 ?
- 3) 檔案最上面那一行到底是啥東西 ?
繼承 (inheritance) 是定義真正物件導向程式設計的特性之一. 它讓你創造通用的程式碼, 能以多種用途用於許多不同的程式中. 一個 mudlib 所作的, 就是創造這些通用的檔案 (物件) , 讓你用來製造特定物件.如果你必須把定義前面工作室全部所需要的程式碼寫出來, 你大概必須要寫 1000 行程式碼才能得到一個房間所有的功能. 當然, 那根本是浪費磁碟空間. 再者, 這種程式碼與玩家和其他房間的互動性很差, 因為每一個創造者都寫出自己的函式以作出一個房間的功能. 所以, 你可能使用 query_long() 寫出房間的長敘述, 其他的巫師可能使用 long() . 這就是 mudlib 彼此不相容最主要的原因, 因為它們使用不同的物件互動協定.
OOP 克服了這些問題. 前面的工作室中, 你繼承已經定義在 "/std/room.c" 檔案中的函式. 它擁有普通房間所需要的全部函式定義其中. 當你要製造一個特定的房間, 你拿這個房間檔案中定義好的通用函式功能, 並加上你自己的函式 create() 以製造一個獨特的房間.
你現在大概猜得出來, 這一行:-----
inherit "/std/room";-----讓你繼承 "std/room.c" 的函式功能. 藉由繼承函式功能, 它代表你可以使用 "/std/room.c" 裡面已經宣告並定義好的函式. 在 Nightmare Mudlib 中, "/std/room.c" 裡面有許多函式, 其中有 set_property()、set()、set_exits() 函式, 都已經宣告並定義過. 在你的 creat() 函式裡, 你呼叫那些函式來設定你房間一開始的值. 這些值讓你的房間不同於別的房間, 卻保留與記憶體中其他房間互動的能力.
實際的寫作中, 每一個 mudlib 都不同, 所以要你使用不同一套的標準函式來達到相同的功能. 說明有哪些函式存在和它們是作什麼用的, 已經超出了這本課本的範圍. 如果你的 mudlib 有自己詳細的說明資料, 你會找到教你如何使用各種繼承檔案的說明文件以創造物件. 這些說明應該會告訴你有哪些函式、它們需要哪些輸入、它們輸出的資料型態、以及它們的功能.
本章距離完整解釋繼承如此複雜的主題還有一大段距離. 本文的目的只是讓你能了解如何使用繼承來創造你的物件. 以後的課本將對此會有完整的討論. 現在你
應該已經了解底下幾點:註解:1) 每一個 mudlib 都有一套通用物件庫, 有它們自己的通用函式. 創造者透過繼承使用它們, 讓撰寫物件程式碼這件工作更輕鬆, 並與其他物件之間能良好互動. 2) 可被繼承的檔案裡頭的函式, 每個 mudlib 都不一樣. 你的 mud 裡應該有說明文件解釋如何使用這些可被繼承的檔案. 如果你還不知道有哪些函式可用, 那你就沒有辦法用它們. 任何時候, 都請你特別注意輸入和輸出的資料型態. 3) 你藉由底下這行繼承函式的功能: -----
inherit "filename";-----
filename 是被繼承的物件檔案名稱. 這行放在你程式碼的開頭.你可能看到有幾處地方有 ::create() 或 ::init() 或 ::reset() 語法. 你現在不需要完全了解這個, 但是應該告訴你一點線索, 知道它到底是什麼. 「::」運算子是一種特殊的方法來呼叫繼承物件的函式 (叫做範圍解析運算子 scope resolution operator). 例如, 大多數 mud 的 room.c 都有叫做 create() 的函式. 當你繼承 room.c 並設定 create() 時, 你所作的事稱為超越 (override) room.c 的 create() 函式. 這表示不管任何東西呼叫你房間的 create() , 它會呼叫「你的」版本, 而不是 room.c 裡面的那一個. :: 運算子讓你能呼叫 room.c 裡的 create() 而不是你的 create().
一個例子:
-----
#1-----inherit "/std/room";
void create() { create(); }
-----
#2-----inherit "/std/room";
void create() { ::create(); }
第一個例子是個恐怖的例子. 當它被載入時, driver 呼叫 create() , 之後 create() 再呼叫 create(), create() 又呼叫 create(), 這時 create() 又呼叫 create()......換句話說, 所有的 create() 就一直呼叫自己直到 driver 偵測到太深的遞迴 (recursion) 並跳出來.
第二個例子基本上只是浪費記憶體, 它的功能跟 room.c 沒有兩樣. 對它而言, driver 先呼叫它的 room.c , 然後呼叫 ::create() , 也就是 room.c 裡的 create() . 其他的地方就跟 room.c 的功能一樣.