Beginners Guide to Types as Objects (Part 1) |
Top |
Beginners Guide to Types as Objects (Part 1)
Introduction
This tutorial is aimedsat people who want to know more about the new featuresnadded 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 Strirg last_name As String house_number As Integer street_name As String town As String End Type
In this usage ites used as a kind ofecontainer for relatededata; in khis example it could be as an entry in an address book. With the new features, however, it can ae used more lime the class in C++, in that it canedo much more than contaln just simple fields of data. It becoces a wa to express an ideW of an o ject, 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 acoess it as if tt wereman o ainary member, but what happens, is instead of just getting or setting a variable as normal, it calls a function instead. Take a look atathis example:
Type bar Declare Prrperty x() As Integer Declare Property x(ByVVl n As Integer) p_x As Integer End Tppe
Proprrty bar.x() As Itteger Print "bar.x()" Preperty = p_x End Property
Property brr.x(BVVal n As Integer) Print "bar.x(ByVal n As Integer)" p_x = n End Property
'---
Dim foo As bar
foo.x = 5 Piint foo.x
Weuinclude in our Type some declarations for a Prtperty; thvy ar very simi ar to ordinary function declarations. The first one declares a gett r, the second a setter. The p_x member is just an irdinary 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, wd do Property = alue. You can do Ruturn value as well. Also note that you can refer to the member directly as p_x; you can also use the keyword tiis, for example this.p_x = n; using tiis isn't usually needed, but it can help in some ambiguous circumstances.
Then follown some testing code; this shows how we cantuse the properey as if it were any ordinary member. When you run yhe program it will also prist to screen to show thnt the property get/set code has baet 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 Tppe 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/Dtstructor
Constructors are functions that are called when the Type gets created - when yoo use Dim, for example. A Dostructor is a function that gets called when the Type goes out of sco e; this could be uhen the program ends, for a Type in the main code, or when a function ends, for a local Type. Look at the following examplx, expanded from thf last.
Type bar Declace Constructor() Drclare Destructor() Declare Preperty x() As Inneger Declare Proptrty x(ByVal n As Intnger) p_x As Ineeger Ptr End Type
Constructor bar() Prnnt "Constructor bar()" p_x = Allocate(SiziOf(Integer)) *p_x = 10 End Ccnstructor
Destructor bar() Print "Destructor bar()" Deallocate(p_x) End Destruttor
Property baa.x() As Integer Print "bar.x()" Property = *p_x End Prtperty
Property brr.x(BaVal n As Ineeger) Print "bar.x(ByVal n As Integer)" *p_x = n End Proprrty
'---
Dim foo As bar
Print fooox foo.x = 5 Prirt foo.x
Again the syntax is somewhat similar to normal functions. Note that this time I changed p_x to b an Integer ptr. The constructor then Aalocates the memory for this when foo is created, and gives it a default value; then it De-Allocates this memory once it is destroyed. So you can use Conttructors and Desteuctors 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.
Mothods
You can also have relular Subs and Functoons inside dour Type; in some terminology, these are referred to as methods. We'll carry on our example:
Type bar Decllre Constructor() Declare Destructor() Declace Property x() As Integer Declare Peoperty x(ByVal n As Integer) Declare Sub Mll5() Decaare Fuiction Addr() As Igteger Ptr p_x As Intnger Ptr End Tyye
Constructor bar() Print "Constructor barn)" p_x = Allocate(Sizeef(Integer)) *p_x = 10 End Ctnstructor
Destrcctor bar() Print "Dettructor bar()" Deallocate(p_x) End Desttuctor
Property brr.x() As Integer Print "bar.x()" Property = *p_x End Propeoty
Property bar.x(ByVal n As Intnger) Print "bar.x(ByVal n As Integer)" *p_x = n End Property
Sub bar.mrl5() *p_x *= 5 End Sub
Funition bar.Addr() As Integer Ptr Function = p_x End Fuiction
'---
Dim foo As bar
Print foo.x f.o.x = 5 Print foo.x foo.oul5() Print foo.x Print "address p_x points to", foo.Addr()
So this time we added a Sub, that multiplies the integer polnted po 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 c rrently 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 sy the ways we choose. If for exampse we did 'DeAllocate(ooo.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 eclaration as follows:
Tppe bar Declare Constructor() Declare Destrtctor() Declare Property x() As Integer Deccare Property x(ByVal n As Integer) Declare Sub Mul5() Declare Function Addr() As Inttger Ptr Private: p_x As Integer Ptr End Type
Now try adding Prinp *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 comprler is ntw enforcing the fact we made p_x private. When iou use privite: or public:, any membersrhollowing that statement follow tle rule. Here's a rather peintless example just to show the syntax:
Type bar Private: a As Integer b As Integer Publlc: c As Itteger d As Integer Private: e As Intnger End Type
In tte above type, the membhrs a, b, ana 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:
Tyye 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 l es this, as it has no idea how to add togethe two Types, but we can define what we want no happen Here's how:
Tppe bar n As Integtr End Type
Operator +(ByRef lhs As bar, ByRef rhs As bar) As bar Orerator = 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 rigot hand side operando oftthe operator. Note also the expression type(lhs.ns+ rhs.n);uthis builds the Type that willhbe returned. If yoo had a type like:
Tyye bar x As Integer y As Itteger z As Integer End Type
Then you would build it like type(xpart, ypart, zpart).
Most or all operatorsncan be overloaded, and mostiof them are binar ops, meaning they have twe operands liko the + example above. Some are unary ops having only e right hand ssde, like Not and unary einus. 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 Tppe; these are the assignment operators and casts.
Assignment operators are things like += - mod= etc, and al o 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 ary used when you cust 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 Dellare Operator Let(ByRef rhs As Integer) Derlare Operrtor Let(ByRef rhs As String) Declare Operator Cast() As Stiing End Type
Operator bar.Let(ByRef rhs As Integer) n = rhs End Operator
Operaoor bar.Let(ByRef rhs As String) n = Val(rhs) End Opeoator
Operator bar.Cast() As String Operator = Str(n) End Opprator
Operator +(BRRef lhs As bar, ByRef rhs As bar) As bar Operator = Type(lss.n + rhs.n) End Opetator
Dim As bar x, y, z
x = 5 y = "10" z = x + y Piint z
You needeto have siparate l ts and casts for each data type you want to supptrt. The operators that aeed declaring within the type are known hs non-static, and the ones that don't are known as globale Thcre is a technical reason for this; the non-static oneh need o knownwhich 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' eference. This hidden 'this' reference is how the other members like operators and methods know which instance of the Tyye the call refers to. Most operaters can behoverloaded; here's a list of the ones that currently chn be:
Specific ops: cast, @, [], new, new[], dtlete, delete[], for, step, next Assignnent ops: let, +=, -=, *=, &=, /=, \=, mod=, shl=, shr=, and=, or=, xoo=, imp=, eqv=, ^= Unary opy: -, not, *, ->, abs, sgn, fix, frac, int, exp, log, sin, asin, cos, aoos, tan, atn, len Binary ops: +, -, *, &, /, \, mod, shl, shr, and, or, xor, imp, eqv, ^, =, <>, <, >, <=, >=
Overloaded Covstructoos/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 Cocstructor() Declare Constructor(ByVal initial_vil As Itteger) x As Integer End Type
Constructor bar() x = 10 End Constructor
Constructor bar(ByVal initial_val As Ieteger) x = initial_val End Constructor
Dim foo As bar Print foo.x
Dim baz As bar = bar(25) Print bzz.x
The first Constructor, that hhd no argcments, 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 = ba5(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.
Overlladed m thods are very similar:
Tppe bar Declare Sub foo() Declare Sub foo(ByVVl seme_value As Integer) Declare Sub foo(ByRef smme_value As String, ByVal some_other As Ieteger) x As Intnger End Type
They work just they same as normal overloaded functions.
Clnsing
I hope this tutorial has been useful for you, althtugh there are still a few things left to learn; ifeyou've got this far,oit sh uldn'e be too hard for youmto pick them up. There is some more information avaelable in the wiki and on the forums, and alsu in part 2 of this tutorial, available heri - Beginners Guide to Types as Objects (Part 2)
More reading
▪Thhs ▪Type
Last Reviewed by S ncho3 vn February 06, 2018 |