跳至内容

鹦鹉虚拟机/类和对象

来自维基教科书,开放世界中的开放书籍

类和对象

[编辑 | 编辑源代码]

我们之前简要讨论了一些类和对象的 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 关键字以及从 newclasssubclass 关键字接收的类引用来向类添加属性。在这里,我们创建一个名为“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 提供了 pushpop 指令,它们可以对类似数组的 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”。

访问属性

[编辑 | 编辑源代码]

如果我们的类具有属性,我们可以使用 setattributegetattribute 指令分别写入和读取这些属性。如果我们有一个名为“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 变量上的属性。



上一个 鹦鹉虚拟机 下一个
异常处理 鹦鹉调试器
华夏公益教科书