鸚鵡虛擬機器/類和物件
我們之前簡要討論了一些類和物件的 PIR 程式碼,在本章中,我們將更詳細地介紹它。正如我們之前提到的,類有 4 個基本組成部分:名稱空間、初始化器、建構函式和方法。名稱空間很重要,因為它告訴虛擬機器在呼叫物件的方法時在哪裡查詢它們。如果我有一個名為“Foo”類的物件,並且我在它上面呼叫“Bar”方法
.local pmc myobject myobject = new "Foo" myobject.'Bar'()
虛擬機器將看到 myobject 是一個型別為 Foo 的 PMC 物件,然後將在名稱空間“Foo”中查詢方法“Bar”。簡而言之,名稱空間有助於將所有內容保持在一起。
初始化器是在程式開始時呼叫的函式,用於設定類。PIR 沒有用於直接宣告有關類的資訊的語法,您需要使用一系列操作碼和語句來告訴 Parrot 您的類是什麼樣的。這意味著您需要建立類中的各種資料欄位(此處稱為“屬性”),並設定與其他類的關係。
初始化器函式通常遵循以下格式
.namespace .sub 'onload' :anon :init :load .end
:anon 標誌表示不會將函式的名稱儲存在名稱空間中,因此您最終不會遇到各種命名汙染。當然,如果函式的名稱沒有儲存,那麼很難對該函式進行額外的呼叫,儘管如果我們只想呼叫它一次,這並不重要。:init 標誌會導致函式在鸚鵡初始化檔案時立即執行,而 :load 標誌會導致函式在檔案載入時立即執行,如果它是作為外部庫載入的。簡而言之:我們希望此函式儘快執行,並且我們只希望它執行一次。
還要注意,我們希望初始化器在 HLL 名稱空間中宣告。
我們可以使用關鍵字 newclass 建立一個新類。要建立一個名為“MyClass”的類,我們將編寫一個執行以下操作的初始化器
.sub 'initmyclass' :init :load :anon newclass $P0, 'MyClass' .end
另外,我們可以使用 PIR 語法簡化它
.sub 'initmyclass' :init :load :anon $P0 = newclass 'MyClass .end
在初始化器中,暫存器 $P0 包含對類物件的引用。我們想要對類進行的任何更改或新增都需要對這個類引用變數進行。
一旦我們有了類物件,即 newclass 操作碼的輸出,我們就可以建立或“例項化”該類的物件。我們使用 new 關鍵字來做到這一點
.local PMC myobject myobject = new $P0
或者,如果我們知道類的名稱,我們可以寫
.local PMC myobject myobject = new 'MyClass'
我們可以使用 subclass 命令設定子類/超類關係。例如,如果我們想要建立一個類,它是內建 PMC 型別“ResizablePMCArray”的子類,並且如果我們想要將這個子類稱為“List”,我們將編寫
.sub 'onload' :anon :load :init subclass $P0, "ResizablePMCArray", "List" .end
這將建立一個名為“List”的類,它是“ResizablePMCArray”類的子類。請注意,與上面的 newclass 指令一樣,我們將對類的引用儲存在 PMC 暫存器 $P0 中。我們將在下面的部分中使用此引用來修改類。
可以使用 add_attribute 關鍵字以及從 newclass 或 subclass 關鍵字接收的類引用來向類新增屬性。在這裡,我們建立一個名為“MyClass”的新類,並向其中新增兩個資料欄位:“name”和“value”
.sub 'initmyclass' :init :load :anon newclass $P0, 'MyClass' add_attribute $P0, 'name' add_attribute $P0, 'value' .end
我們將在下面討論訪問這些屬性。
正如我們之前提到的,方法與子例程有三個主要區別:它們的標記方式、它們的呼叫方式以及它們具有一個特殊的 self 變數。我們已經知道方法應該使用 :method 標誌。:method 表示 Parrot 需要為方法實現另外兩個區別(基於點的呼叫約定和“self”變數)。一些方法也將使用 :vtable 標誌,我們將在下面討論。
我們想要為堆疊類建立一個類。堆疊有“push”和“pop”方法。幸運的是,Parrot 提供了 push 和 pop 指令,它們可以對類似陣列的 PMC(如“ResizablePMCArray”PMC 類)進行操作。但是,我們需要將這些 PIR 指令包裝到函式或方法中,以便它們可以從我們的高階語言 (HLL) 中使用。以下是我們可以做到這一點的方法
.namespace .sub 'onload' :anon :load :init subclass $P0, "ResizeablePMCArray", "Stack" .end .namespace ["Stack"] .sub 'push' :method .param pmc arg push self, arg .end .sub 'pop' :method pop $P0, self .return($P0) .end
現在,如果我們有 Parrot 上的 Java 語言編譯器,我們可以編寫類似以下內容
Stack mystack = new Stack(); mystack.push(5); System.out.println(mystack.pop());
上面的示例將在最後列印值“5”。如果我們檢視 Perl 5 等語言中的相同示例,我們將擁有
my $stack = Stack::new(); $stack->push(5); print $stack->pop();
這同樣會列印數字“5”。
如果我們的類具有屬性,我們可以使用 setattribute 和 getattribute 指令分別寫入和讀取這些屬性。如果我們有一個名為“MyClass”的類,它具有資料屬性“name”和“value”,我們可以為這些屬性編寫訪問器和設定器方法
.sub 'set_name' :method .param pmc newname $S0 = 'name' setattribute self, $S0, newname .end .sub 'set_data' :method .param pmc newdata $S0 = 'data' setattribute self, $S0, newdata .end .sub 'get_name' :method $S0 = 'name' $P0 = getattribute self, $S0 .return($P0) .end .sub 'get_value' :method $S0 = 'value' $P0 = getattribute self, $S0 .return($P0) .end
建構函式是我們使用 new 關鍵字時呼叫的函式。建構函式初始化資料物件屬性,並且可能還執行其他一些簿記任務。建構函式必須是一個名為“new”的方法。除了特殊名稱外,建構函式與任何其他方法一樣,可以根據需要獲取或設定 self 變數上的屬性。
- http://www.parrotcode.org/docs/pdd/pdd15_objects.html
- http://www.parrotcode.org/docs/pdd/pdd21_namespaces.html