Руководство для начинающих по теме: Типы как объекты
 

Введение

Этот учебник направлен на людей, которые хотят узнать больше о новых возможностях, добавленных в Type, обычно именуемый «тип как объект" и "ООП штучки". Новые возможности описаны для людей , которые не понимают данную тему, но хотят понять. Type FreeBASIC представляет собой совокупность типов данных такие, как структуры в C, или запись в Паскале. Вот простенький типичный 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++, потому что может сделать гораздо больше, чем только содержать простые поля данных. Это дает способ выражать идею объекта, и делает объектно-ориентированного программирование гораздо проще. Теперь мы будем рассматривать эти новые возможности.

Property (свойства)

Мы начнем со свойств. При добавлении свойства в тип, доступ к нему будет такой же как будто это рядовой элемент типа, но вместо простой установки\получения значения в переменную-элемент , происходит вызов функции. Взгляните на этот пример:

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


Мы включаем в наш Type некоторые декларации для Property; они очень похожи на обычные функции. Первая из них декларирует процедуру получения, вторая процедуру установки. p_x элемент является просто обычным элементом Integer.

Далее мы пишем код для свойства; Опять же синтаксис очень похож на запись нормальных функций. Обратите внимание на то, как мы возвращаем значение: вместо Function = value, мы используем Property = value. Так же можно использовать такую запись: Return value. Также, обратите внимание, что вы можете обратиться к элементу напрямую как p_x; Вы также можете использовать ключевое слово this, например this.p_x = n; В реале в ключевом слове this нет необходимости, но оно может помочь в некоторых неоднозначных обстоятельствах.

Далее следует некоторый тестовый код; Он показывает, что мы можем использовать свойство так же, как если бы это был любой рядовой элемент. При запуске программы, результат будет выводиться на экран, чтобы показать, что свойства получения и установки были вызваны.

Сейчас этот код довольно тривиальный, но это только для теста, в конце концов вы увидите , что данный метод можно применять и в практических целях. Представьте, к примеру, вы пишете GUI и Type представляет кнопку на экране, вы могли бы сделать button.text = "Hello World!" и сделать код свойства обновления экрана, чтобы показать изменения. Или может быть вы используете Type поддерживающий какой-то список; Вы могли бы сделать list.size += 10 и затем положить некоторый код в ваше свойство, чтобы увеличить размер списка.

Constructor/Destructor (Конструктор\Деструктор)

Конструкторы - это функции, которые вызываются, когда создается Type например при использовании Dim. Деструктор - это функция, которая вызывается, когда 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. Конструктор выделяет память для p_x при создании foo и присваивает p_x значение по умолчанию; Деструктор же освобождает эту память после того, как foo будет уничтожен. Поэтому вы можете использовать конструкторы и деструкторы, для установки значений и для того, чтобы подчищать за объектами. Конечно же это опять тривиальный пример, но он может подсказать принцип создания списков в более удобной форме.

Методы

Вы можете также иметь регулярные Subs и Functions внутри вашего 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
  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, в которой умножается на пять(5) число , на которое указывает указатель p_x, а так же функция, которая возвращает адрес памяти, который содержит указатель.

Private/Public (Приватный доступ\Открытый доступ)

По умолчанию всех элементы типа имеют открытый доступ; Это означает, что мы можем читать, записывать и вызывать их. Однако иногда может потребоваться сделать их приватными. Возьмем, к примеру, наш элемент p_x; в настоящее время мы можем написать так: Print *foo.p_x и это позволит нам напечатать значение по указателю 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 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 bar
  x As Integer
  y As Integer
  z As Integer
End Type


тогда вы бы могли построить его так type(xpart, ypart, zpart).

Большинство или почти все операторы могут быть перегружены, и большинство из них являются бинарными, то есть они имеют два операнда, как в случае с +. Некоторые из них унарные, имеющие только правую часть, как Not и унарный минус. Их можно определить так: 'Operator 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 and cast для каждого типа данных, который вы хотите поддерживать. Операторы, которые должны быть продекларированы в пределах типа известны как нестатические, и те, которые не известны как глобальные. Есть технические основания для этого; нестатическим нужно знать какой экземпляр (в техническом жаргоне; в нашем примере выше, можно сказать, что x является экземпляром bar) типа они имеют в виду, и это достигнуто путем скрытой ссылки 'this'. Благодаря этой ссылке 'this' другие элементы , такие как операторы и методы знают, к какому Type относится вызов. Большинство операторов могут быть перегружены; Вот список тех, которые в настоящее время могут быть перегружены:

Присваивание:
let, +=, -=, *=, /=, \=, mod=, shl=, shr=, and=, or=, xor=, imp=, eqv=, ^=
Унарные:
-, not, @, *, ->
Бинарные:
+, -, *, /, \, mod, shl, shr, and, or, xor, imp, eqv, ^, =, <>, <, >, <=, >=

Перегруженные Конструкторы/Методы

Как и обычные функции, конструкторы и методы нашего типа могут быть перегружены. Для конструкторов это предоставляет способ для указания сведений о том, как должен быть создан экземпляр. Вот короткий пример:

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)

Больше информации