Beginners Guide to Types as Objects
Introduction
This tutorial is aimed at people who want to know more about the new features added to `Type`, commonly being referred to as 'types as objects', and 'that OOP stuff'. It aims to walk you through these new features, so is aimed at people who don't really understand it yet, but want to learn. A `Type` in FreeBASIC is an aggregate data type, like a struct in C, or a record in Pascal. Here's just a short sample of typical `Type` usage.
`
Type person_info
first_name As String
last_name As String
house_number As Integer
street_name As String
town As String
End Type
`
In this usage it's used as a kind of container for related data; in this example it could be as an entry in an address book. With the new features, however, it can be used more like the class in C++, in that it can do much more than contain just simple fields of data. It becomes a way to express an idea of an object, and this makes object oriented programming much simpler. We will now look at these new features.
Property
We'll start by looking at property. When you add a property to a `Type`, you access it as if it were an ordinary member, but what happens, is instead of just getting or setting a variable as normal, it calls a function instead. Take a look at this example:
`
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
`
We include in our `Type` some declarations for a `Property`; they are very similar to ordinary function declarations. The first one declares a getter, the second a setter. The `p_x` member is just an ordinary `Integer` member.
Next we write the code for the properties; again, the syntax is very similar to that of normal functions. Note the way we return a value: instead of `Function = value`, we do `Property = value`. You can do `Return value` as well. Also note that you can refer to the member directly as `p_x`; you can also use the keyword `this`, for example `this.p_x = n`; using `this` isn't usually needed, but it can help in some ambiguous circumstances.
Then follows some testing code; this shows how we can use the property as if it were any ordinary member. When you run the program it will also print to screen to show that the property get/set code has been called.
Now this code is fairly trivial, but as you get used to the idea you'll see it can be put to some good uses. Imagine as an example you are writing a GUI, and the TYPE represents a button on the screen, you could do `button.text = "Hello World!"`, and make the property code update the screen to show the changes. Or maybe you are using the `Type` to maintain some kind of list; you could do `list.size += 10` and then put some code in your property to make the list larger.
Constructor/Destructor
`Constructor`s are functions that are called when the `Type` gets created - when you use `Dim`, for example. A `Destructor` is a function that gets called when the `Type` goes out of scope; this could be when the program ends, for a `Type` in the main code, or when a function ends, for a local `Type`. Look at the following example, expanded from the last.
`
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
`
Again the syntax is somewhat similar to normal functions. Note that this time I changed `p_x` to be an `Integer ptr`. The constructor then `Allocate`s the memory for this when `foo` is created, and gives it a default value; then it `De-Allocate`s this memory once it is destroyed. So you can use `Constructor`s and `Destructor`s to set things up for you, then clean up once its finished with. Again a trivial example, but bring back the example of some kind of list, and having it set the list up for you, and clean it up when it's finished with can be quite handy.
Methods
You can also have regular `Sub`s and `Function`s inside your `Type`; in some terminology, these are referred to as methods. We'll carry on our example:
`
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()
`
So this time we added a `Sub`, that multiplies the integer pointed to by `p_x` by five, and a function that gets the memory address that the pointer holds.
Private/Public
By default all of the members of the bar type are public; that means that we can read/write or call them. However, sometimes you might want to make them private. Take for example our member `p_x`; we can currently do `Print *foo.p_x`, and it will allow us to print the value it points to. We might want to make it private, so that only the members of the `bar` type (the constructor, destructor, property, and methods) can access it. That way we can make sure we only deal with `p_x` by the ways we choose. If for example we did '`DeAllocate(foo.p_x)`' in our main code, then when the destructor runs, it would try to free it again, known as a 'double free'. Change the `Type` declaration as follows:
`
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
`
Now try adding `Print *foo.p_x` to the main code and compile it. You'll get a message from fbc "`error 173: Illegal member access, found 'p_x' in 'Print *foo.p_x'`", showing that indeed the compiler is now enforcing the fact we made `p_x` private. When you use `private:` or `public:`, any members following that statement follow the rule. Here's a rather pointless example just to show the syntax:
`
Type bar
Private:
a As Integer
b As Integer
Public:
c As Integer
d As Integer
Private:
e As Integer
End Type
`
In the above type, the members `a`, `b`, and `e` are private; `c` and `d` are public.
Operator overloading
Operator overloading is a way of telling the compiler what to do in the case where we want to perform some kind of operation involving our `Type`. Take this example:
`
Type bar
n As Integer
End Type
Dim As bar x, y, z
z = x + y
`
Now normally the compiler will throw an error when it sees this, as it has no idea how to add together two `Type`s, but we can define what we want to happen. Here's how:
`
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
`
In this code, I use `lhs` and `rhs` to refer to the left and right hand side operands of the operator. Note also the expression `type(lhs.n + rhs.n)`; this builds the `Type` that will be returned. If you had a type like:
`
Type bar
x As Integer
y As Integer
z As Integer
End Type
`
Then you would build it like `type(xpart, ypart, zpart)`.
Most or all operators can be overloaded, and most of them are binary ops, meaning they have two operands like the + example above. Some are unary ops having only a right hand side, like `Not` and unary minus. They would be done like '`Operator Not(ByRef rhs As bar) As bar`'.
There are some special cases where they have to be declared inside the `Type`; these are the assignment operators and casts.
Assignment operators are things like `+= -= mod=` etc, and also `Let`. `Let` is used when you do an assignment like:
`
Dim As bar foo
Dim As Integer x
foo = x
`
And casts are kind of the reverse; they are used when you cast to another datatype like:
`
Dim As bar foo
Dim As Integer x
x = foo
`
Heres a short example using `Let` and `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
`
You need to have separate lets and casts for each data type you want to support. The operators that need declaring within the type are known as non-static, and the ones that don't are known as global. There is a technical reason for this; the non-static ones need to know which instance (in the technical jargon, in our example above, we would say that `x` is an instance of `bar`) of the `Type` they are referring to, and this is accomplished by a hidden '`this`' reference. This hidden '`this`' reference is how the other members like operators and methods know which instance of the `Type` the call refers to. Most operators can be overloaded; here's a list of the ones that currently can be:
Assignment ops:
`let`, `+=`, `-=`, `*=`, `/=`, `\=`, `mod=`, `shl=`, `shr=`, `and=`, `or=`, `xor=`, `imp=`, `eqv=`, `^=`
Unary ops:
`-`, `not`, `@`, `*`, `->`
Binary ops:
`+`, `-`, `*`, `/`, `\`, `mod`, `shl`, `shr`, `and`, `or`, `xor`, `imp`, `eqv`, `^`, `=`, `<>`, `<`, `>`, `<=`, `>=`
Overloaded Constructors/Methods
As with normal functions, our `Type`'s constructor and methods can be overloaded. For constructors, this provides a way to specify details on how the instance should be constructed. Here's a short example:
`
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
`
The first `Constructor`, that had no arguments, is known as the default constructor. This sets up `foo.x` to an initial value of 10. However, we have also specified another constructor that will accept an initial value. Note the way we ask for this to be called `Dim baz As bar = bar(25)`. You can also leave out the default constructor, and then you will always have to specify the initial value using the constructor that takes an argument. You can't have an overloaded destructor, because there's no way to manually choose which one would be called.
Overloaded methods are very similar:
`
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
`
They work just they same as normal overloaded functions.
Closing
I hope this tutorial has been useful for you, although there are still a few things left to learn; if you've got this far, it shouldn't be too hard for you to pick them up. There is some more information available in the wiki and on the forums, and also in part 2 of this tutorial, available here - Beginners Guide to Types as Objects (Part 2)
More reading

- `Property`
- `Constructor`
- `Destructor`
- `Operator`
- `This`
- Anonymous Types
- Types as Objects
- `Public:`
- `Private:`
- `Protected:`