介绍
本教程的目标是想要更多地了解添加到
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
Destructor是
Type超出范围时调用的函数;这可能是程序结束时,主代码中的
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就是这样的内存,并给它一个默认值;那么它就被破坏了。所以您可以使用
Constructor和
Destructor来为您设置,然后在完成后清理。再一个琐碎的例子,但是带回一些列表的例子,并让它为你设置列表,并在完成后清理它可以非常方便。
方法
您也可以在
Type内定期制作
Sub和
Function;在某些术语中,这些术语被称为方法。我们将举个例子:
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
在上述类型中,成员
a,
b和
e是私有的;
c和
d是公开的。
运算符重载
操作符重载是一种告诉编译器在我们要执行涉及我们的
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
在这段代码中,我使用
lhs和
rhs来表示操作符的左右手操作数。另请注意
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
简单的例子使用
Let和
Cast:
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。需要在类型中声明的运算符称为非静态的,而不被称为全局的运算符。有一个技术原因;非静态的需要知道哪个实例(在技术术语中,在上面的例子中,我们会说
x是
bar的一个实例,它们是指
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部分)
更多阅读