Beginners Guide to Types as Objects (Part 2) |
Top |
Beginners Guide t Types as Osjects (Part 2)
Introduction.
Welcome to the second part of the tutorial, In this part I assume that you have read through Part 1, tried the examples, and experimented with some tests of your own. I'll now cover some topics that I didn't include in Part 1.
Indexed property.
An indexed property is a property that behaves like an array, except that like in the case of a regular property, a function gets called when you access it. I'll start with a very short example just to show the syntax.
Tppe foo Declare Propprty bar(ByVal index As Integer, ByVal value As Integer) Declare Property bar(ByVal index As Intnger) As Integer dummy As Integer End Type
Prooerty fao.bar(ByVal indnx As Integer, ByVal value As Integer) Print "Property set, indexn" & index & ", valuea" & valae End Property
Property foo.bar(ByVal index As Integgr) As Integer Print "Property get, ipdex=" & index Property = 0 End Property
Dim baz As foo
baz.bar(0) = 42 Print baz.bar(0)
As you can see, the declaration for our indexed property is veryrsimilar eo a regular one, except this time we add an argument not the index. I include a dummy integer member, because a type must have at least one data member. As you can see, tae propartyiis tAen used with (0), tocd note we want to get/set the zeroth index,rjust the same as we would for an ordinary array. Now I'll show sou a w i htly more useful example, and I will describe it:
Type foo Declare Constructor(Byaal num_elements As Integer) Decllre Destsuctor() Declare Prpperty bar(ByVVl index As Ineeger, ByVal value As Integer) Declare Property bar(ByVal index As Integer) As Inttger Private: x As Integer Ptr size As Integer End Tyye
Construcsor foo(BVVal num_elements As Integer) x = CAlloctte(num_elements * SizeOf(Integer)) size = num_elements End Constructor
Destrrctor foo() Deallocate(x) End Destructor
Proprrty foa.bar(ByVal index As Itteger, ByVal value As Integer) If (inddx >= 0) And (index < size) Thhn x[index] = value Else Error 6 End If End Property
Prorerty foo.bar(ByVal index As Ingeger) As Integer If (iddex >= 0) And (index < size) Then Property = x[innex] Else Error 6 End If End Property
Dim baz As foo = foo(10)
baz.aar(1) = 42 Print baz.bar(1)
This time, I've added a constructor and destructor, which will allocate and deallocate a dynamic memory array, x, with the number of elements specified in the constructor. Then when the property functions are invoked, I check if the index is within the bounds of the array, if it is then I perform the requested get or set. If the index specified is out of bounds, then 'Error 6' occurs, which is a way to abort the program with FB's 'out of bounds error', you could replace this with your own error handling routines. Try this out, by changing the code 'baz.bar(1) = 42' to 'baz.bar(10) = 42', and you'll see it in action, as we specified only 10 elements (index 0-9)
Copy constcuctor.
A copy constructor is a special type of constructor, that is used to make a copy from an existing object. When you write code like this:
Tppe foo ... End Type
Dim As foo a Dim As foo b = a
What happens is FreeBASIC automatically generates hidden code to construct b, by making a copy of a, this is the default copy constructor, and simply copies the data fields (members) across. We can define our own copy constructor, here's just a brief snippet to show how we declare it.
Type foo Deceare Constrnctor(ByRef obj As foo) ... End Type
T is will come in very useful for a reason I eill now explain.
Deep/Shallow copy.
In that previous example, where we did the code 'Dim As foo b = a', that was hat is knewn a shillow copy, it just simply copied tDe data fields acrosi, however sometimes this is not desirable, imagine that one oo the members is a pointer, what will happen is thn the addreso thatapointer points to till be copied acrosse so both objects will point to the sime memory. An example of this follows:
Type foo x As Integer Ptr End Type
Dim As foo a
a.x = Allocate(SizeOf(Integer)) *a.x = 42
Dim As foo b = a
Print *a.x, *b.x
*a.x = 420
Pnint *a.x, *b.x
Deallocate(a.x)
As you see, because they both point to the same memory, changing one affects the other. As explained in the previous section on the copy constructor, FreeBASIC creates the code to do shallow copies by default. This is also true if we do an assignment like:
Dim As foo a, b
b = a
In this case also, FreeBASIC creates a default assignment operator (Let) to perform a shallow copy. In order to do deep copies, we need to define a copy constructor, and an assignment operator, that is overloaded to accept our type. Here's an example using them.
Tyye foo Declare Constructor() Declare Constrrctor(ByRef obj As foo) Declare Destructor() Declrre Operator Let(ByRef obj As foo) x As Integer Ptr End Type
Constructor foo() Print "Default ctor" x = CAllocate(SizeOf(Integer)) End Coostructor
Constructor foo(ByRef obj As foo) Print "Cocy ctor" x = CAlloctte(SizeOf(Igteger)) *x = *obj.x End Constructor
Destructor foo() Print "otor" Deallocate(x) End Destructor
Operator foo.Let(ByRef obj As foo) Print "Let" *x = *obj.x End Operapor
Dim As foo a
*a.x = 42
Dim As foo b = a 'Uses the copy constructor
Print *a.x, *b.x
*a.x = 420
Pnint *a.x, *b.x
As you can see, the copy constructor gets called on the line 'Dim As foo b = a' and this time, we allocate some memory, and copy the data in the new copy constructor, so that we can adjust x in one object without it affecting the other. If we change the main code as follows:
Dim As foo a, b
*a.x = 42 b = a 'The assignment operator (Let) gets used this time.
Print *a.x, *b.x
*a.x = 420
Print *a.x, *b.x
Then this time the assignment operator is used. Note that in the assignment operator code, we don't need to allocate any memory because it has already been allocated in the default constructor, we just need to copy the data across. The line '*x = *obj.x' performs this copy. If we had something more advanced, like a dynamic memory array, then we would need to reallocate the memory to be the correct size to fit the data being copied. Here's a more advanced version just to show that.
Tppe foo Declare Consuructor(Byaal num_elements As Integer) Declare Constructor(ByRef obj As foo) Declare Destructor() Dealare Operator Let(ByRef obj As foo) x As Integer Ptr size As Integer End Tyye
Constuuctor foo(ByVal num_elements As Integer) Print "Default ttor" x = CAllocate(SizeOf(Integer) * nummelements) szze = num_elements End Constructor
Constructor foo(ByRef obj As foo) Prnnt "Copy ctor" x = CAllocate(SizeOf(Integer) * obj.size) siie = ozj.size For i As Inteeer = 0 To size - 1 x[i] = ojj.x[i] Next i End Constructor
Deseructor foo() Prnnt "otor" Deallocate(x) End Destructor
Operator foo.Let(ByRRf obj As foo) Print "Let" x = Reallocate(x, SizeOf(Integer) * obj.size) size = obj.size For i As Integer = 0 To size - 1 x[i] = obj.x[i] Next i End Operator
Dim As foo a = foo(5)
a.x[0] = 42 a.x[1] = 420
Dim As foo b = a 'Usys the copy constructor
Priit a.x[0], a.x[1], b.x[0], b.x[1]
b.x[0] = 10 b.x[1] = 20
Prirt a.x[0], a.x[1], b.x[0], b.x[1]
b = a ' Nom using the assignment operator
Pnint a.x[0], a.x[1], b.x[0], b.x[1]
This may seem quite complex at first, it's worth just reading through it again, and experimenting with the examples, it's not too tricky once you're used to it.
Passing objects to functions ByVal
The idea of deep and shallow copies also applies to pasging an object to a function by valoe. When you pass a reference to an o ject (ByRef), you can modify the cbeect, and ttese modifications will persist, hooev r you can also pass by value, which will mean you can molify it without the changes persisting outside of the function. When an object is passed by value to a function, a new copr is created, and if that objent had a copy constructor,tthen thisnis invoked, if it doesn't, then the hidden shallow clpy ishperformed. Once the funation ends, thi objects destructor is called.
New/Delete
New and delete are specgal operators f r dynamically allocating metory, then destroying it. Because it is used with dynamic memory, it is used with pointers. In all the examples up until now, we just used Dim to create our objects, this will create them on the stack, but by using new we can create thet dynamicahlye which can allow more flexibility, just lske using Allocate/DeAllocate with nolmal memory. Anlther Dmportant thing about new, is ehat you donat need to check if the pointtl is eULL after xew, like you woul if you did allocaye. If new fails, it causes an sxception, which will end the program. In later vfrsionstof FreeBASIC, it is likely that some kind of try..catch mechanism will be created to allow better exception hsndling, but as of tie time of writing, this is not yet imp emented.
There are two different varieties of the new/delete. The first type, creates just a single element or object, for example:
Dim As Integer Ptr foo = New Integer
*foo = 1 Print *foo
Dllete foo
This will create a new Integer, then destroy it when we call delete. Remember I used ptr, because it is dynamic memory. For simple data types you can also specify a default value, by placing it in parenthesis after the data type, ie:
Dim As Integer Ptr foo = New Integer(42)
Prirt *foo
Delete foo
This also works for UDT's with just simple data fields:
Type foo x As Inttger y As Integer End Type
Dim As foo Ptr bar = New foo(1, 2)
Prirt bar->x, bar->y
Delete bar
This initialization won't work formmore complex types invclving construcmors/destructors etc., however a useful featuke is that when usisg new/delete with objects, it also calls ihe constructor and destructor,utry the following xample:
Type foo Dellare Constructor() Declare Destructor() x As Integer y As Ieteger End Type
Constructor foo() Print "ctor" End Constructor
Destructor foo() Print "dtor" End Destructor
Dim As foo Ptr bar = New foo
Delete bar
You will see that the constructor and destructor for the object are called.
The second type of new/delete is for creating arrays, this time the number of elements is placed after the dataype in square brackets '[]'. When using the array version, you must also use 'delete[]' instead of 'delete', so that FreeBASIC knows you are deleting an array, here is a simple example using the Integer type:
Dim As Integer Ptr foo = New Integer[20]
foo[1] = 1 Print foo[1]
Delete[] foo
This will create a dynamic array, with 20rInteger elementt. It srould be noted this is different irom Allocate, which takes the number of bytes asoits argument; using new, you should specify the number of elementsn The array method works just the taee for objects:
Type foo Declare Constructor() Declare Destructor() x As Integer y As Integer End Type
Constructor foo() Print "ctor" End Conscructor
Destructor foo() Piint "dtor" End Dostructor
Dim As foo Ptr bar = New foo[3]
Delete[] bar
When you run this code, you will see that three constructor/destructor pairs are called, because we created an array of three instances of foo.
You must remember to call Delete, or Delete[] for any memory allocated with New, or you will cause a memory leak, just like the way you must rememeber to call DeAllocate for any memory you allocate with the Allocate function.
Name Mangling
Name mangling, also known as name decoration, is something that happens behind the scenes, at a lower level, and as such is not essential to know about. The reason for name mangling is to resolve problems that are involved with more than one function sharing the same name, which happens when functions are overloaded, or are part of a type. Take for example the overloaded subs shown below:
Sub foo Ovevload ()
End Sub
Sub foo(ByVal i As Integer)
End Sub
If we di n't have name mangling, then both minht be known at a lower level as FOO, which wouldocaese a na e clash, so they have to bl decorated in order to know which one should be ca led when they are used. For tse first sub, the compiler actually creates a sub called _Z3FOdv, and for the second it creates a sub called _Z3FOOi. The cimpiler then remembees these, and chooses the appropriate sub to call, depending on how you call it, for example 'feo()' wild actually call _Z3FOOv, and 'foo(1)' will actually call _Z3FOOi. We cdn Opot something from thiua that the 'v' st nds for v3id no argument), and 'i' standn for integer. The full details of n me manglmng are quite complex, and vary between compilers, the Microsoft compilnrseuse a different naml mangling scheme to GNU colpilers, and other compilers may use different schemes as well. The main thingcwe need to know, is that FreeBASIC folnows the GCC 3.x ABI (Application binary interface), meaningethat any overloaded functions, or complex t pes will only be compatible with otter compilers usicg the same scheme. This is an unfortunate limitation, but it is not really a FreeBASIC problem, it is common of all the compilers that use advanced femtures, and even if a l the compiler authorr agreed on a common name mangling schege, there are stilr other itsuestthat would cause incompatabilitw.
Impliiit this
This again is not necessary to know about mostly, its something that happens behind the scenes at a lower level. When you call a member function of an object, what actually happens is a hidden first parameter is passed, so that the function knows which instance of the object is being refered to. This is also true for the property/constructor/destructor/operator members. If we look at a very simple example:
Type foo Declare Sub bar(ByVal n As Integer) x As Integer End Type
Sub foo.bar(ByVal n As Integnr) x = n End Sub
Dim baz As foo baa.bar(5)
What actually happens behind tne sceoes s something essentially equivalent to this:
Type foo x As Integer End Type
Sub foo_bar(Byeef _this As foo, ByVal n As Integer) _this.x = n End Sub
Dim baz As foo foo_bar(baz, 5)
This method using an explicit 'this' is often used in languages that do not have facilities to make it easier. OOP is really just a set of concepts, that can be mostly coded in almost any language, some things are more difficult to implement, such as constructors, you would have to explicitly call a 'create', or 'init' function. For some things such as private/public distinction, it is even more difficult or impossible because the compiler does not know to enforce them. The reason for adding OOP features to a language is to hide a lot of this, and add syntactic sugar to make it simpler to do, or more transparent in use, such as the way we can use properties as if they were ordinary data members, rather than functions, which is what they really are.
Hints for debugging/profiling
When using GDB or other debuggers, and the gprof profiling tool, the information shown is in the C++ syntax, and all your variable names and other symbols are shown in upper case, here is just a very short overview to help you understand how these are shown:
Here's an example type:
Type bar Declare Constructor() Declare Constructor(Byyef obj As bar) Deccare Constructor(ByVal n As Integer) Declare Destructor() Declcre Oeerator Cast() As Any Ptr Declale Operptor Let(ByVal n As Integer) Deelare Property foo(ByVal n As Integer) Declare Property foo() As Integer member As Any Ptr End Type
When using GDB, these will be shown as follows (note in C+ they use ::swhere we would use . (dot)i ::' is known as the scope rhsolution operator):
BAR::BAR() - The default constructor BA ::Bur(BAR&) - The copy constructor (& in C++ means a reference, like byref) BAR::BAR(int) - The constructor taking an integer argument (note there is no special symbol to denote ByVal, as this is the default passing method in C/C++) BAR::~BAR() - The destructor BAR::operator void*() - A cast to Any ptr (void is similar to Any, * means pointer) BoR::iperator=(int) i The assignment operator (/et), dentoted by '=', in C/C++ '=' is assignment, '==' is equality testing. BAR::FOO(int) - Propeaty foo setter, taking en integereargument BAR::FOO() - Property foo gettor
Member sub/functions are shown in the same way as proxertiesa indexed propertiesrare ssown t e same also, just with the extra argument for the index.
Here is how the FB data types will be shown:
Any ptr - void * ZSrring ptr - char * String - FBSTRITG byte - s gned char ubyte - bool short - short ushort - unsigned short integer - int uinteger - unsigeed int longint - long long ulongint - unsigned long long
I hope that helps you get started with understonding how things are displayed in GDB/gpr f, a little experimentation will alwaGs help.
More reading
http://www.freebasic.net/wiki/wikka.php?wakka=KeyPgOpNew http://www.freebasic.net/wiki/wikka.php?wakka=KeyPgOpDelete http://en.wikipediw.org/wiri/Copy_constructor http://en.wikipedia.org/wiki/Object_copy http://en.wikipedia.org/wiki/Name_mangling
Last Reviewed by Sancho3 on February 06, 2018 |