Beginners Guide to Types as Objects
 

介绍

本教程的目标是想要更多地了解添加到Type的新功能的人,通常被称为“作为对象的类型”和“那些OOP东西”。它旨在让您了解这些新功能,因此针对的人还不了解,但希望学习。FreeBASIC中的Type是聚合数据类型,如C中的结构体,或Pascal中的记录。以下只是典型Type用法的简短示例。

Type person_info
  first_name As String
  last_name As String
  house_number As Integer
  street_name As String
  town As String
End Type


在这个用法中,它被用作相关数据的一种容器;在这个例子中,它可以是通讯簿中的条目。然而,使用新功能,它可以更像C ++中的类使用,因为它可以做的不仅仅是简单的数据字段。它成为表达对象概念的一种方法,这使得面向对象的编程变得更加简单。现在我们来看这些新功能。

属性

我们先看财产。当您将属性添加到Type时,您可以访问它,就像它是普通成员一样,但是会发生什么,而不是正常获取或设置变量,它会调用一个函数。看看这个例子:

Type bar
  Declare Property x() As Integer
  Declare Property x(ByVal n As Integer)
  p_x As Integer
End Type

Property bar.x() As Integer
  Print "bar.x()"
  Property = p_x
End Property

Property bar.x(ByVal n As Integer)
  Print "bar.x(ByVal n As Integer)"
  p_x = n
End Property

'---

Dim foo As bar

foo.x = 5
Print foo.x


我们在Property中提及了一些Property的声明;它们与普通函数声明非常相似。第一个声明一个吸气剂,第二个是一个吸气剂。p_x成员只是普通的Integer成员。

接下来我们编写属性的代码;语法与正常函数非常相似。注意我们返回一个值:而不是Function = value,我们做Property = value.你也可以做Return value.另请注意,您可以直接参考会员p_x;您也可以使用关键字this,例如this.p_x = n;通常不需要使用this,但它可能有助于某些模糊的情况。

然后遵循一些测试代码;这显示了我们如何使用属性,就像它是任何普通成员一样。当您运行程序时,它也将打印到屏幕,以显示属性get / set代码已被调用。

现在这个代码是相当微不足道的,但是随着你习惯了这个想法,你会看到它可以被用于一些很好的用途。想象一下,您正在编写GUI,TYPE表示屏幕上的按钮,您可以执行button.text = "Hello World!",并使属性代码更新屏幕以显示更改。或者也许你使用Type来维护某种列表;您可以执行list.size += 10,然后在属性中放置一些代码,使列表更大。

Constructor/Destructor

Constructor是创建Type时使用Dim时调用的函数。A DestructorType超出范围时调用的函数;这可能是程序结束时,主代码中的Type或功能结束时,对于本地Type.看下面的例子,从最后一个展开。

Type bar
  Declare Constructor()
  Declare Destructor()
  Declare Property x() As Integer
  Declare Property x(ByVal n As Integer)
  p_x As Integer Ptr
End Type

Constructor bar()
  Print "Constructor bar()"
  p_x = Allocate(SizeOf(Integer))
  *p_x = 10
End Constructor

Destructor bar()
  Print "Destructor bar()"
  Deallocate(p_x)
End Destructor

Property bar.x() As Integer
  Print "bar.x()"
  Property = *p_x
End Property

Property bar.x(ByVal n As Integer)
  Print "bar.x(ByVal n As Integer)"
  *p_x = n
End Property

'---

Dim foo As bar

Print foo.x
foo.x = 5
Print foo.x


语法又与普通函数有些相似。请注意,这次我将p_x更改为Integer ptr.当创建foo时,构造函数Allocate就是这样的内存,并给它一个默认值;那么它就被破坏了。所以您可以使用ConstructorDestructor来为您设置,然后在完成后清理。再一个琐碎的例子,但是带回一些列表的例子,并让它为你设置列表,并在完成后清理它可以非常方便。

方法

您也可以在Type内定期制作SubFunction;在某些术语中,这些术语被称为方法。我们将举个例子:

Type bar
  Declare Constructor()
  Declare Destructor()
  Declare Property x() As Integer
  Declare Property x(ByVal n As Integer)
  Declare Sub Mul5()
  Declare Function Addr() As Integer Ptr
  p_x As Integer Ptr
End Type

Constructor bar()
  Print "Constructor bar()"
  p_x = Allocate(SizeOf(Integer))
  *p_x = 10
End Constructor

Destructor bar()
  Print "Destructor bar()"
  Deallocate(p_x)
End Destructor

Property bar.x() As Integer
  Print "bar.x()"
  Property = *p_x
End Property

Property bar.x(ByVal n As Integer)
  Print "bar.x(ByVal n As Integer)"
  *p_x = n
End Property

Sub bar.mul5()
  *p_x *= 5
End Sub

Function bar.Addr() As Integer Ptr
  Function = p_x
End Function

'---

Dim foo As bar

Print foo.x
foo.x = 5
Print foo.x
foo.mul5()
Print foo.x
Print "address p_x points to", foo.Addr()


所以这次我们添加了一个Sub,它将p_x指向的整数乘以5,并获取指针所在的内存地址的函数。

Private/Public

默认情况下,所有条形类型的成员都是public;这意味着我们可以读/写或者调用它们。但是,有时您可能希望将其隐藏。以我们的会员p_x为例我们目前可以做Print *foo.p_x,它将允许我们打印它指向的值。我们可能希望使其成为私有的,以便只有bar类型的成员(构造函数,析构函数,属性和方法)可以访问它。这样我们就可以确定我们只按照我们选择的方式处理p_x.例如,如果我们在主代码中做了“DeAllocate(foo.p_x)”,那么当析构函数运行时,它会尝试再次释放它,称为“双倍空”。更改Type声明如下:

Type bar
  Declare Constructor()
  Declare Destructor()
  Declare Property x() As Integer
  Declare Property x(ByVal n As Integer)
  Declare Sub Mul5()
  Declare Function Addr() As Integer Ptr
Private:
  p_x As Integer Ptr
End Type


现在尝试添加Print *foo.p_x到主代码并编译它。您会收到来自fbc“error 173: Illegal member access, found 'p_x' in 'Print *foo.p_x'”的消息,显示编译器现在正在执行我们p_x私有的事实。当您使用private:public:时,该语句之后的任何成员都遵循规则。这是一个非常无意义的例子,只是为了显示语法:

Type bar
Private:
  a As Integer
  b As Integer
Public:
  c As Integer
  d As Integer
Private:
  e As Integer
End Type


在上述类型中,成员abe是私有的; cd是公开的。

运算符重载

操作符重载是一种告诉编译器在我们要执行涉及我们的Type的某种操作的情况下该怎么做的一种方式。举个例子:

Type bar
  n As Integer
End Type

Dim As bar x, y, z

z = x + y


现在通常编译器会看到这个错误,因为它不知道如何将两个Type添加在一起,但是我们可以定义我们想要发生的事情。就是这样:

Type bar
  n As Integer
End Type

Operator +(ByRef lhs As bar, ByRef rhs As bar) As bar
  Operator = Type(lhs.n + rhs.n)
End Operator

Dim As bar x, y, z

x.n = 5
y.n = 10
z = x + y
Print z.n


在这段代码中,我使用lhsrhs来表示操作符的左右手操作数。另请注意type(lhs.n + rhs.n);这将构建将被返回的Type.如果你有一个类型:

Type bar
  x As Integer
  y As Integer
  z As Integer
End Type


那么你会建立它像type(xpart, ypart, zpart).

大多数或全部操作符可以重载,大多数是二进制操作,这意味着它们有两个操作数,像上面的+示例。一些只有右手边的一元操作,如Not和一元减号。他们将像“操作符 Not(ByRef rhs As bar) As bar”完成。

有一些特殊情况,必须在Type内声明;这些是赋值运算符和转换。

作业操作符是+= -= mod=等,还有Let.当您进行如下任务时,将使用Let

Dim As bar foo
Dim As Integer x
foo = x


而演员则是相反的;当您转换为其他数据类型时,它们将被使用,如:

Dim As bar foo
Dim As Integer x
x = foo


简单的例子使用LetCast

Type bar
  n As Integer
  Declare Operator Let(ByRef rhs As Integer)
  Declare Operator Let(ByRef rhs As String)
  Declare Operator Cast() As String
End Type

Operator bar.Let(ByRef rhs As Integer)
  n = rhs
End Operator

Operator bar.Let(ByRef rhs As String)
  n = Val(rhs)
End Operator

Operator bar.Cast() As String
  Operator = Str(n)
End Operator

Operator +(ByRef lhs As bar, ByRef rhs As bar) As bar
  Operator = Type(lhs.n + rhs.n)
End Operator

Dim As bar x, y, z

x = 5
y = "10"
z = x + y
Print z


您需要为要支持的每种数据类型分别提供let和cast。需要在类型中声明的运算符称为非静态的,而不被称为全局的运算符。有一个技术原因;非静态的需要知道哪个实例(在技术术语中,在上面的例子中,我们会说xbar的一个实例,它们是指Type,而这是完成的通过隐藏的“this”引用。这个隐藏的“this”引用是其他成员喜欢运算符和方法的方式,它知道Type调用的哪个实例。大多数操作符可能超载;以下是目前可以列出的列表:

作业操作:
let, +=, -=, *=, /=, \=, mod=, shl=, shr=, and=, or=, xor=, imp=, eqv=, ^=
一般操作:
-, not, @, *, ->
二进制操作:
+, -, *, /, \, mod, shl, shr, and, or, xor, imp, eqv, ^, =, <>, <, >, <=, >=

重载构造器/方法

与正常功能一样,我们的Type的构造函数和方法可以重载。对于构造函数,这提供了一种指定如何构建实例的详细信息的方法。以下是一个简短的例子:

Type bar
  Declare Constructor()
  Declare Constructor(ByVal initial_val As Integer)
  x As Integer
End Type

Constructor bar()
  x = 10
End Constructor

Constructor bar(ByVal initial_val As Integer)
  x = initial_val
End Constructor

Dim foo As bar
Print foo.x

Dim baz As bar = bar(25)
Print baz.x


第一个没有参数的Constructor被称为默认构造函数。这将foo.x设置为初始值10。但是,我们还指定了另一个将接受初始值的构造函数。注意我们要求这样称为Dim baz As bar = bar(25)的方式。您也可以省略默认构造函数,然后总是使用构造函数来指定初始值,该构造函数需要一个参数。你不能有一个重载的析构函数,因为没有办法手动选择哪一个将被调用。

重载方法非常相似:

Type bar
  Declare Sub foo()
  Declare Sub foo(ByVal some_value As Integer)
  Declare Sub foo(ByRef some_value As String, ByVal some_other As Integer)
  x As Integer
End Type


他们的工作方式与正常的重载功能相同。

闭幕

我希望本教程对您有用,尽管还有一些要学习的东西;如果你有这么远,你不应该太难接你了。有更多的信息可以在维基和论坛上获得,也可以在本教程的第2部分中找到 - 初学者类型对象指南(第2部分)

更多阅读