You are on page 1of 31

Object Oriented Programming in Delphi - A Guide for Beginners

Web Tech Training & Development


Article: OOP | Author: Rick Spence

Object Oriented Programming in Delphi A Guide for Beginners


Object Pascal, Delphis underlying language, is a fully object oriented language. Simply, this means that the language allows the programmer to create and manipulate objects. In more detail, this means that the language implements the four principles of object oriented programming:
q q q q

Data Abstraction Encapsulation Inheritance Polymorphism

As youll see, these are complicated names for pretty simple ideas. In teaching 100s of Delphi programmers, Ive found that getting to grips with Object Oriented programming is the difference between just getting by with Delphi, and really making the most of the product. In this article and the next, Ill introduce Delphi programmers to the Object Oriented features in Object Pascal, and show how to take advantage of them in your own applications. Even if youve used Delphi for a while, you may find these articles a useful review - its amazing how much you can do with Delphi without really understanding the principles of the language. This article starts of with a couple of simple definitions. It starts by describing object-oriented programming in general terms, then precisely defines two terms youve no doubt heard object and class. It then proceeds to look at the language mechanisms you use to work with objects, and shows how to ensure your objects are released correctly (i.e. that your program does not have any resource leaks). Following this, the article
http://www.webtechcorp.co.uk/web-developer-training-delphi-article-oop.htm (1 of 31)30/1/2009 00:42:29

Object Oriented Programming in Delphi - A Guide for Beginners

discusses, in detail, the syntax you use to create classes; youll see a real-world class you could immediately put to use in your applications.

What is OOP? Whats an Object? How about Class?


Object Oriented Programming (OOP for short), is all about writing programs that manipulate objects. Delphi, along with C++ and Java, is a fully object oriented language. As youll see, the principles of object oriented programming are the same in all these languages, though of course the syntax is different. Once youve learned the principles, however, no matter which language you learn them with, youll find that knowledge transfers easily to other languages. Concepts such as inheritance and data abstraction are the same in C++, Java, and Delphi its just the language syntax that differs. Whether youve used an object oriented language or not, youve probably heard the terms object and class thrown around. A class is a programming construct developers use to specify and implement new data types. All languages come with predefined data types such as integers and strings. Object oriented languages allow programmers to create their own data types, such as students, accounts, and menus. Since these data types are not built-in to the language or the underlying computer hardware, we call these abstract data types. The language mechanism programmers use to do this is called a class. A class, then, is a specification and implementation of an abstract data type. If youve never used an object oriented programming language before, this concept will be new to you. Look at it like this. Whatever language you have used before, youre used to using that languages data types strings, integers, reals, booleans, etc. A class allows a programmer to create his or her own data types which they can proceed to use just like the predefined data types. Delphis VCL (Visual Component Library) is simply a collection of classes written by Borland / Inprise staff. Things youve no doubt used in your Delphi applications such as forms, tables, queries, radio buttons, check boxes etc. are all classes defined in
http://www.webtechcorp.co.uk/web-developer-training-delphi-article-oop.htm (2 of 31)30/1/2009 00:42:29

Object Oriented Programming in Delphi - A Guide for Beginners

the VCL. As youll see, you can create your own classes and use them in the same way. Later in this article well cover the syntax in detail. For now, consider the following class declaration: Type TStudent = Class FLName : Integer; FFName : Integer; FTel : String; End; This declaration declares a new data type called TStudent. The TStudent data type is represented by 3 pieces of information the last name (FLName), the first name (FFName), and the telephone number (FTel). Your program can now proceed to declare variables of type TStudent: Var Student1 : TStudent; Student2 : TStudent; The only difference between this, and declaring variables with types that are declared in the VCL, as in: Var StringList1 : TStringList; IniFile1 : TIniFile; is that with the former you have declared your own data type (TStudent), whereas with the latter the VCL declared the data types (TStringList and TIniFile). In order for the compiler to find the declaration of the classes you use, your program must explicitly use the unit declaring the classes. If you created a program unit containing a form, Delphi will automatically generate a USES clause for you which lists the most commonly used units inside the VCL. This is why your program can refer to classes such as forms, checkboxes, and push buttons without explicitly listing the units those classes reside in. If you didnt create a form, however, i.e.
http://www.webtechcorp.co.uk/web-developer-training-delphi-article-oop.htm (3 of 31)30/1/2009 00:42:29

Object Oriented Programming in Delphi - A Guide for Beginners

you have an empty unit, Delphi did generate this USES clause and if you attempt to reference any VCL classes you will receive compilation errors. The same rules apply to your own classes. If one unit in your application references a class declared in another unit, the first unit must list the second unit in its USES clause. Weve discussed classes, but whats an object? Thats easy. An object is simply an instance of a class a variable whose data type is a class. Student1 is an Object. So is Student2. Likewise StringList1 and IniFile1. The term object, then, is a term used to describe any variable whose type is a class. Objects, of course can be of any class, so when describing an object you usually use its class name as well thus you will talk about stringlist objects, form objects, student objects, etc.

Working with Objects


Before we get into writing our own classes, a quick review on how to work with classes and objects. Well use Delphis TStringList class (declared in the VCL) to illustrate our points. The first step is to declare a variable of type TStringlist. Var StringList1 : TStringList; // TStringList is the class // StringList1 the object Where do place this declaration? It depends upon where you want to use the object (its scope), and how long you want to use it for (its lifetime). If you only want to use it inside a subroutine then declare it inside that subroutine: Procedure Test; Var StringList1 : StringList; Begin // Work with StringList1 here... End;

http://www.webtechcorp.co.uk/web-developer-training-delphi-article-oop.htm (4 of 31)30/1/2009 00:42:29

Object Oriented Programming in Delphi - A Guide for Beginners

In this case, as soon as the procedure Test terminates, you cannot access the object; you can only access it from within that subroutine. If you want an object to have a wider scope and a longer lifetime you must declare it outside a subroutine. If you place it inside the units implementation section, outside of any sub-routine, the object is visible throughout that unit, but only inside that unit. If you place it inside the units interface section, then the object is visible both throughout this unit, and other units which use this unit. Now, regardless of where you declare the object, your program is responsible for both allocating and releasing its memory. This is the main difference between working with objects and working with simple variables. When you work with objects you are responsible for both allocating and releasing their memory. When you work with simple variables, after declaring the variable you can use it immediately, as in: Var i : Integer; Begin i := 10; Simply declaring the variable allocates its memory. When working with an object, however, you must first allocate its memory: Var Student1 : TStudent; Begin // Allocate memory for the object To allocate the memory you must call a special routine called a constructor. A classs constructor can be named anything, and indeed classes can have more than one constructor. Most classes, however, declare one constructor called Create. Youll look at writing your own constructors later in this article when you learn how to write your own classes. For now we are using predefined classes, so we only need to be concerned with their constructor. The constructor for the TStringList
http://www.webtechcorp.co.uk/web-developer-training-delphi-article-oop.htm (5 of 31)30/1/2009 00:42:29

Object Oriented Programming in Delphi - A Guide for Beginners

class is called Create. To call the constructor you prefix the constructor name with the class name, as in: TStudent1.Create; The constructor is actually a function which returns a pointer to the memory it allocated. So, to allocate the memory for the object, you call Create, using the following syntax: Var Student1 : TStudent; Begin // Allocate memory for the object note the general form: // <Object> := <ClassName>. <ConstructorName>; Student1 := TStudent.Create; This is called instantiating the class. Remember the form of the call to the constructor: <Object> := <ClassName>. <ConstructorName>; The biggest mistake people make when getting started with Delphis objects is forgetting to call the classs constructor, or calling it incorrectly. Once youve allocated the memory for the class you can then access its data using the dot operator, as in: Student1 := TStudent.Create; Student1.FLname := Spence; Student1.FFName := Rick; If you try and access the data without first allocating the memory (i.e. forgetting to instantiate the class) you will receive run-time errors. You can of course work with multiple objects: Var Student1 : TStudent;

http://www.webtechcorp.co.uk/web-developer-training-delphi-article-oop.htm (6 of 31)30/1/2009 00:42:29

Object Oriented Programming in Delphi - A Guide for Beginners

Student2 : TStudent; Begin // Allocate memory for the objects Student1 := TStudent.Create; Student2 := TStudent.Create; and each object has its own data. In this example, Student1 has three pieces of data associated with it, and so does Student2: Student1 := TStudent.Create; Student2 := TStudent.Create; Student1.FLName := Spence; Student2.FLName := Brown; Now, you are also responsible for releasing the objects memory. To do this you call another routine called free. However, you must prefix free with the object name, as in: Student1.Free; Note this is not symmetrical. You prefix the constructor name with the class name, but prefix free with the object name. In the next article youll see that this difference is to do with calling a piece of code to work on an object (a regular method), and calling a piece of code to work on a class (a class method). Heres an entire routine which declares, instantiates, and releases a TStringList object. Procedure Test; Var StringList1 : StringList; Begin // Call the constructor to allocate the memory StringList1 := TStringList1. Create; // Work with stringlist1 here... // Release its memory here
http://www.webtechcorp.co.uk/web-developer-training-delphi-article-oop.htm (7 of 31)30/1/2009 00:42:29

Object Oriented Programming in Delphi - A Guide for Beginners

StringList1.Free; End; In this example we allocated and freed the memory in the same routine. This is fine in this case, as the object is only visible inside this routine. If the object were visible throughout the unit, however, you have to decide when to release its memory. Its very common to have objects exist as long as a form. That is, a form may need to use a stringList so you need to create the stringList when you create the form, and you need to free the stringlist when the form is freed. To do this you would instantiate the stringlist class (that is, call its constructor) in the forms onCreate event, and release the stringList (call free) in the forms onDestroy event. The point is, its your responsibility to allocate and free the memory for the object if you forget to release the memory you have what is called a resource leak. Youve allocated the memory but never released it. Will you notice this in your programs? It depends upon how much memory the object requires and how often you instantiate its class. If the object requires 2K of memory and you allocate this every time the user presses a certain push button, your application will rapidly grind to a halt with a memory exhausted error and youll need to reboot the computer to reclaim the memory. If the object only requires a few bytes of memory and you only instantiate it a couple of times you will not notice it. In summary, then, you are responsible for allocating and freeing your objects memory, and its not quite as simple as you might think, as the next section shows

Ensuring your objects memory is released


Consider the following code fragment: Procedure Test; Var i, j : Integer; stringList1 : TStringList; Begin
http://www.webtechcorp.co.uk/web-developer-training-delphi-article-oop.htm (8 of 31)30/1/2009 00:42:29

Object Oriented Programming in Delphi - A Guide for Beginners

stringList1 := TStringList. Create; i := 10; j := 0; i := i div j; // Line 10 Exception generated here stringList1.Free; // Line 11 never executed End; We intentionally generate a divide by zero exception on line 10. If you enter this code and use the debugger to single step through it, youll see that Delphi does not execute line 11. After the exception is detected on line 10, Delphi handles the exception and returns to the applications event loop. Your memory is not released. Of course, this is a contrived example but in general, you must take care when allocating memory for objects that any exceptions will not prevent your calls to free from being executed. Borland / Inprise recommend - and I strongly concur - that you should always bracket your Create / Free calls inside a Try / Finally construct. The Try / Finally construct is part of standard Pascal, and heres how it works. You use Try to denote the start of a block of code. You use Finally to denote a second block of code, then the word End to indicate the end of the entire construct, as in: Try <First Block of code> Finally <Second Block of code> End; If any statement inside the first block generates an exception, Delphi executes the code inside the second block before handling the exception. If the first block does not generate an exception, the second block is still executed. Thus, the second block is guaranteed to be executed regardless of whether an exception occurs or not. So, to ensure your objects memory is released, place the call to Free inside the Finally section. Heres the general form: <Object> := <ClassName>. <ConstructorName>
http://www.webtechcorp.co.uk/web-developer-training-delphi-article-oop.htm (9 of 31)30/1/2009 00:42:29

Object Oriented Programming in Delphi - A Guide for Beginners

Try // Use the object here Finally <Object>.Free; End; Note that the call to the constructor precedes the Try. This is in case the constructor itself fails. If the constructor fails, Delphi does not allocate the memory for the object. If the call to the constructor was after the Try, Delphi would run the code inside the finally block, and you would be attempting to free an object which had no memory. By placing the call to the constructor before the Try, if the constructor itself fails the code inside the finally block is not executed. If you need to allocate and release more than one object, your use of Try / Finally is a little more complex. Consider the following code fragment: Var Student1 : TStudent; StringListl : TStringList; Begin // Allocate memory for the objects StringListl := TStringList. Create; Student1 := TStudent.Create; Try // Work with Student1 & StringList1 Finally Student1.Free; StringList.Free; End; Does this code guarantee that both objects are freed? No. If the constructor for TStudent fails, your program does not enter the Try block, therefore the finally block is not called, and the memory for StringList1 is not released. One solution is to have two Try / Finally blocks: Var Student1 : TStudent;

http://www.webtechcorp.co.uk/web-developer-training-delphi-article-oop.htm (10 of 31)30/1/2009 00:42:29

Object Oriented Programming in Delphi - A Guide for Beginners

StringListl : TStringList; Begin // Allocate memory for the objects StringListl := TStringList. Create; Try Student1 := TStudent.Create; Try // Work with Student1 & StringList1 Finally Student1.Free; End; Finally StringList.Free; End; This works, but is tedious; and imagine the code if you had 3 or more objects to create. One trick you can employ relies on that fact the Free will not free an object that is nil. The source code to Delphis Free is basically: // Delphis Free If theObjectBeingReleased <> Nil Then ReleaseTheMemory; // Self. Destroy Before freeing the objects memory, Free first ensures the object is not nil. Does this mean that the following will work: Var Student1 : TStudent; Begin // Forgot to instantiate TStudent Student1.Free; The answer depends upon the value of Student1 when the call to Free is made. Is Student1 nil? No. In Delphi, variables are not given initial values the actual value of Student1 depends upon what is on the processor stack in the location occupied by Student1 when the routine is called. Now, the

http://www.webtechcorp.co.uk/web-developer-training-delphi-article-oop.htm (11 of 31)30/1/2009 00:42:29

Object Oriented Programming in Delphi - A Guide for Beginners

following will work: Var Student1 : TStudent; Begin Student1 := Nil; // Forgot to instantiate TStudent Student1.Free; How does this help with our problem of allocating a series of objects and guaranteeing that they are freed? Well, it means we can write the following: Student1 := Nil; Try Student1 := TStudent.Create; Finally Student1.Free; End; And even if the constructor fails, the call to Free will not; Student1 was explicitly given the value Nil before the constructor fails. The constructor fails does not allocate the memory for Student1 so Student1 retains its value of Nil, and the call to Free does not fail. If we extend this to the problem of allocating several objects we can write: Var Student1 : TStudent; StringListl : TStringList; Begin Student1 := Nil; StringList1 := Nil; Try StringListl := TStringList. Create; Student1 := TStudent.Create; // Work with Student1 & StringList1 Finally Student1.Free; StringList.Free;

http://www.webtechcorp.co.uk/web-developer-training-delphi-article-oop.htm (12 of 31)30/1/2009 00:42:29

Object Oriented Programming in Delphi - A Guide for Beginners

End; The former solution i.e. nesting Try / Finally blocks is classically a better solution, but the latter, i. e. explicitly setting Objects to nil, and relying upon Free not to destroy objects that are Nil, is certainly more convenient. I tend not to use too many third party products with Delphi, but theres one type of add-on product I strongly feel every Delphi developer should use those that check your programs for resource leaks. There are two products which fall into this category Memory Sleuth NuMega bounds checker. Heres how they work. They monitor your programs use of resources - in this case the resource were talking about is memory, but they also monitor other lower level resources such as window handles and device contexts. When your program terminates, if it hasnt released all the resources it allocated, these products give you a list of all such allocations, including the actual line in the source code which allocated the resource. I strongly recommend you pick up one of these products you might be surprised at what you find...

Declaring your own Classes


As you know, a class is a programming construct you use to specify and implement an abstract data type. The class specifies the individual data elements required to store the objects data. Previously we declared our TStudent class as: Type TStudent = Class FLName : Integer; FFName : Integer; FTel : String; End; This class declaration defines the storage requirements for TStudent objects. If you create 4 TStudent objects, each one has the same three properties. That is, the structure of all TStudent objects is identical, and that structure is determined by the class declaration.

http://www.webtechcorp.co.uk/web-developer-training-delphi-article-oop.htm (13 of 31)30/1/2009 00:42:29

Object Oriented Programming in Delphi - A Guide for Beginners

These individual pieces of data used to represent an object in this case FLname, FFName, and FTel, are known by several names. My preference is to call them instance variables. Delphis documentation refers to them as fields (I find this confuses my students because you also refer to columns in database tables as fields). Other names include data members, attributes and properties, although as youll see later, the word property is also used in another way in Delphis object model. If youre familiar with Pascals Record data type, youll see that so far, a class is no different from a record. Both are examples of composite data types that is, data types that contain several pieces of information. What makes a class different from a record is that you can write code to work with a class. The class can define operations, called methods, which the program can perform on objects. Example of methods for a student class might incluce "RegisterForClass", "AddtoTable", "SendInvoice". Look at Delphis help file to see what methods are available for the TStringList and TIniFile classes, for example.

Writing your own methods


There are two parts to writing methods for a class. The first step is to declare the method in the class declaration, much like you declare an instance variable. This tells the compiler the operations you cdan perform on the class. Methods are essentially sub routines and as such are either functions or procedures, and can receive parameters. You must declare them in the class declaration, indicating whether they are procedures or functions, and listing any parameters they take. As an example, heres a simple class which declares four instance variables and three methods: Type TSquare = Class FX, FY : Integer; FWidth : Integer; Caption : String; Function Area : Integer; Procedure MoveLeft(dx, dy : Integer); Procedure MoveRight(dx, dy :
http://www.webtechcorp.co.uk/web-developer-training-delphi-article-oop.htm (14 of 31)30/1/2009 00:42:29

Object Oriented Programming in Delphi - A Guide for Beginners

Integer); End; The next step involves writing code for these methods. Well get to that in a moment first lets look at how users of this class can call these methods: Var Square1 : TSquare; SqWidth : Integer; Begin Square1 := TSquare.Create; Try Square1.Fx := 10; Square1.Fy := 20; Square1.FWidth := 7; SqWidth := Square1.Area; // 49 we hope Square1.MoveLeft(2, 3); Finally Square1.Free; End; As you can see, you call the methods by prefixing the method name with the object name, in exactly the same way you access an objects instance variables. Now, if you have two objects in memory, and you call a method, which objects instance variables does the method use? That is, given: Var Square1 : TSquare; Square2 : TSquare; SqWidth : Integer; Begin Square1 := TSquare.Create; Square1.FWidth := 7; Square2 := TSquare.Create; Square2.FWidth := 5; SqWidth := Square1.Area; // Is this 49 or 25? What is the value of sqWidth? Youd expect it to be
http://www.webtechcorp.co.uk/web-developer-training-delphi-article-oop.htm (15 of 31)30/1/2009 00:42:29

Object Oriented Programming in Delphi - A Guide for Beginners

49 the value of Square1s Width property multiplied by itself. And indeed it is but as youll see there is a little magic involved to get this to work. Methods must work on the data of the object which called it. When you write Square1.Area the area method must work with the data of the Square1 object. Youll see how this works in a moment. So far youve seen how to declare the method, but you must also write the code to implement the method. You place the code for the method in the units implementation section, and write it much like any other subroutine. When you declare the method, however, you must tell the compiler that you are writing a method rather than a stand alone sub-routine. You do this by prefixing the method name with the name of the class, as in: Function TSquare.Area : Integer; and: Procedure TSquare.MoveLeft(dx, dy : Integer); Note how the object which called the method is not explicitly received as a parameter. So how does the method which is essentially a sub-routine access the instance variables of the object which called it? Actually, the object is received as a parameter to the method but you dont see it, nor do you need to declare it. When you write code such as: SqWidth := Square1.Area; // 49 Think of the compiler actually generating the following code: SqWidth := Area( Square1 ) Behind the scenes it is passing the object to the method as a parameter. Inside the method you can

http://www.webtechcorp.co.uk/web-developer-training-delphi-article-oop.htm (16 of 31)30/1/2009 00:42:29

Object Oriented Programming in Delphi - A Guide for Beginners

reference the object which called the method using a predefined identifier called Self. You use Self, then, to access the objects instance variables. When you call the method with: Square1.Area inside the method Self refers to the Square1 object. When you call it with: Square2.Area Self refers to the Square2 object. Heres the entire Area method: Function TSquare.Area : Integer; Begin Result := Self.FWidth * Self. FWidth; End; And here are the methods for MoveLeft and MoveRight: Procedure TSquare.MoveLeft( dx, dy : Integer); Begin Self.Fx := Self.Fx dx; Self.Fy := Self.Fy dy; End; Procedure TSquare.MoveRight( dx, dy : Integer); Begin Self.Fx := Self.Fx + dx; Self.Fy := Self.Fy + dy; End; In most cases Self is optional - that is you can simply refer to the instance variables without using Self, s in: Function TSquare.Area : Integer;

http://www.webtechcorp.co.uk/web-developer-training-delphi-article-oop.htm (17 of 31)30/1/2009 00:42:29

Object Oriented Programming in Delphi - A Guide for Beginners

Begin Result := FWidth * FWidth; End; This works because the compiler knows the class to which a method belongs, therefore it knows when your code is accessing instance variables whether you use Self or not. Theres one exception to this, however, and thats when you have a local variable with the same name as an instance variable. Consider the following (admittedly contrived) example: Function TSquare.Area : Integer; Var FWidth : Integer; Begin Result := FWidth * FWidth; End; Here you have both a local variable and an instance variable called FWidth. In this case the compiler will actually use the local variable and the method will not work. To correct this you would need to explicitly prefix the instance variables with the word Self. Tip After you have declared the methods in your class, press Ctrl Shift C Delphi will generate the method outlines for you.

Naming Conventions
Ive been using a couple of naming conventions which I should explicitly mention. The convention in Delphi is to name all classes starting with the letter T, standing for Type. Thats why weve been using TSquare and TStudent as our class names rather than simply Square and Student. Youll notice that all the classes in Delphis VCL start with the letter T thus you see classes such as TButton, TForm, etc. Another convention Ive been using is to prefix all instance variables with the letter F, standing for
http://www.webtechcorp.co.uk/web-developer-training-delphi-article-oop.htm (18 of 31)30/1/2009 00:42:29

Object Oriented Programming in Delphi - A Guide for Beginners

Field. Youll see the reason for this when we look at something called properties in the next article. For now just follow the conventions blindly!

MRU List Class


Theres a lot more to object oriented programming than weve covered so far, but at this stage a realworld example will help cement these ideas in your mind. We will develop a class (a new data type) called a MRUList to manage a list of most recently used strings. Youve seen MRU lists in many applications:
q

The Microsoft Office products, for example, keep track of the most recently used files you have worked with. Windows Explorer keeps track of the most recently used documents. Delphi keeps track of the most recently used projects.

Well develop a class which users can use to track a list of most recently used strings the user of the class could use this to track the most recently used customers, text files, deleted records, whatever. Because we havent covered all of Delphis objectoriented features yet, our class will start out simple well only use the object-oriented features weve discussed so far. Lets start by declaring the operations we want the class to support well worry about the implementation in a moment. Heres what we need: Query how many elements are in the list The ability to add a string to the MRU list The ability to ask what string is at a certain position When you add a string to the list, it appears at the start of the list. Well decide on a maximum number of elements to store in the list and when the user adds more than that the ones at the end "drop off". Thats why we call it a Most Recently Used list we track the most recently used strings.

http://www.webtechcorp.co.uk/web-developer-training-delphi-article-oop.htm (19 of 31)30/1/2009 00:42:29

Object Oriented Programming in Delphi - A Guide for Beginners

Heres the first declaration of this class Type TMruList = Class Function Count : Integer; Procedure Add( s : String); Procedure GetString( n : Integer) : String End; Given this class declaration, heres how users of the class can use it: Var muList : TMruList; s : String; Begin // Instantiate the class mruList := TMruList.Create; Try // Add items to it mruList.Add(Spence); mruList.Add(Jones); // Access some of the items the most recently // used unit is at zero s := mruList.GetString( 0 ); // s now contains Jones Finally // Remember to free the objects memory mruList.Free; End; End; In this simple example we instantiated the class and freed it in the same routine. If you were using this class with a form, you would instantiate the class in the forms onCreate event, and free the object in the forms onDestroy event. We must now decide how to implement the class. We must decide how to store the strings, how to count the strings, and how to write the methods. The best solution is probably to use Delphis TStringlist to store the strings. When users add an

http://www.webtechcorp.co.uk/web-developer-training-delphi-article-oop.htm (20 of 31)30/1/2009 00:42:29

Object Oriented Programming in Delphi - A Guide for Beginners

element to the MRUList, we simply add it to the start of the stringlist. We will indeed implement this solution in a moment, but we havent covered all the OOP theory we need to do this yet. Heres the problem. We can easily add a TStringList to our class declaration: Type TMruList = Class FMList : TStringList; Function Count : Integer; Procedure Add( s : String); Procedure GetString( n : Integer) : String End; but our class needs to instantiate the stringlist i.e. we need this: FMList := TStringList.Create; Instantiating the MRUList class does not instantiate the TStringList class. You could insist the user of the class instantiates the stringList after instantiating the class: mruList := TMruList.Create; mruList.FMList := TStringList.Create; but this is poor design. You are requiring the user of the class to perform operations in a certain order. Furthermore, the class user must also free the stringlist before freeing the mruList class this is too much responsibility to place on the user. There is a solution to this i.e. you can write the mruList class so that it instantiates and frees the stringList but this requires the use of constructors and destructors and we havent covered that yet. Later in this article well look at changing the class in this manner. For now, then, well use a fixed length array to store the strings, and have a separate variable which keeps track of how many elements are used at any time.

http://www.webtechcorp.co.uk/web-developer-training-delphi-article-oop.htm (21 of 31)30/1/2009 00:42:29

Object Oriented Programming in Delphi - A Guide for Beginners

Listing 1 shows the new class declaration and the implementation of its methods. Const MRUMaxItems = 4; Type TMruList = Class FMList : Array[0..MRUMaxItems 1] of String; FNumItems : Integer; Function Count : Integer; Procedure Add( s : String); Function GetString( n : Integer) : String End; Implementation // Return the number of elements in the MRUList Function TMruList.Count : Integer; Begin Result := Self.FNumItems; End; // Shift all the elements in the list up by one, add new element at the start Procedure TMruList.Add(s : String); Var i : Integer; Begin For i := max(Self.FNumItems, MRUMaxItems - 1] DownTo 1 Do Self.FMList[i] := Self.FMList [i 1]; Self.MList[0] := s; Self.FNumItems := Max(FNumItems + 1, MRUMaxItems); End; Function GetString( n : Integer ) : String; Begin If (n >= 0) and (n <= Self.
http://www.webtechcorp.co.uk/web-developer-training-delphi-article-oop.htm (22 of 31)30/1/2009 00:42:29

Object Oriented Programming in Delphi - A Guide for Beginners

FNumItems - 1) Then Result := Self.FMList[n]; End; Listing 1 First Version of MRUList class

Scope of Instance Variables and Methods


When we talk about the scope of instance variables and methods, we mean where they are visible i.e. who can use them. In the classes youve seen so far, all the methods and instance variables were visible to the class users; this is not good. Consider the TMRUList classs FNumItems instance variable. This a variable used internally by the class to track how many items are in the list. Because this instance variable is visible to the class user, however, theres nothing to stop him / her from changing it directly thus destroying the integrity of our class. You must distinguish then, between instance variables and methods which are visible to class users, and those that should only be used internally by the class. You should only allow the class user to see the variables and methods which constitute the classs interface i.e. those things that are essential in order to use the class. The implementation details of the class its inner workings should be hidden from the class user. This allows the person writing the class to change the implementation without affecting the class user. As long as the interface to the class does not change the class user will not have to change his / her code. This is the second principle of object oriented programming, encapsulation.

Sidebar
Principle of Encapsulation.... To hide things from the class user, the class developer separates his / her class declaration into sections. A section determines the scope of the declarations placed inside it. A class can contain up to four sections, named:
http://www.webtechcorp.co.uk/web-developer-training-delphi-article-oop.htm (23 of 31)30/1/2009 00:42:29

Object Oriented Programming in Delphi - A Guide for Beginners

Public Published Private Protected Which section you place your declarations in determines their scope and how you can use them.

Public
The public section of the class contains things that class users can see. If you dont explicitly name a section it defaults to public thus the method and instance variable scope in the classes youve seen so far has been public.

Private
The private section of the class contains things that class users cannot see. The only code that can see private instance variables and methods are other methods of this class. Private instance variables are contained in each object, but class users cannot access them directly. Listing 2 shows a second version of our MRUList class which places numItems and MList in the private section. We didnt list the code for the methods as those didnt change. Type TMruList = Class Private FMList : Array[0.. MRUMaxItems 1] of String; FNumItems : Integer; Public Function Count : Integer; Procedure Add( s : String); Function GetString( n : Integer) : String End; Listing 2 Second version of MRUList class using Private instance variables Now the user of the class cannot access either FMList of FNumItems directly. We have restricted
http://www.webtechcorp.co.uk/web-developer-training-delphi-article-oop.htm (24 of 31)30/1/2009 00:42:29

Object Oriented Programming in Delphi - A Guide for Beginners

access to these instance variables to the methods of this class. This is private data which the user of the class has no business seeing. This is encapsulation hiding the implementation details of the class.

Protected
The Protected section has to with inheritance so Ill hold off on a detailed description until I cover that in the next article. Briefly, instance variables and methods you place in the protected section are not visible class users; they resemble privates in this regard. The difference between protected scope and private scope is to do with sub classes. Sub class method cannot see anything a superclass declares as private, but they can see things the superclass declares as protected.

Published
The published section of the class is very similar to the public section; they both list instance variables the class user can see. The difference between the two sections is with regard to components. Components are simply classes which you can use within Delphis IDE, which the user can drop onto a form and manipulate visually. Radio buttons, push buttons etc. are examples of components. Not all classes are components, of course. TStringList is not a component, neither is TMRUList (yet we will make it a component later). But all components are classes. When your class is a component, whatever you place in the published section is available in the object inspector. This allows the user to assign values to these instance variables using the object inspector instead of making the same assignments in code. Ill give examples of this in the next article where we look at creating components.

Writing your own Constructors & Destructors


You already know what a constructor is its a special function you call to create an instance of a class. Your classes can declare their own
http://www.webtechcorp.co.uk/web-developer-training-delphi-article-oop.htm (25 of 31)30/1/2009 00:42:29

Object Oriented Programming in Delphi - A Guide for Beginners

constructors in a similar manner to the way in which you declare normal methods. The advantage to writing your own constructors is you can perform initialization when the class is instantiated. For example, consider the following code which instantiates a TSquare class, then proceeds to set some of its instance variables: o := TSquare.Create; o.FX := 10; o.FY := 10; o.FWidth := 5; o.FCaption := First Square; If you wrote your own constructor for the TSquare class it could receive initial values for those parameters and allow the class user to write: o := TSquare.Create( 10, 10, 5, First Square); This is certainly more convenient, but it confers other advantages as well. By providing a constructor the class developer can ensure his / her object is correctly initialized. For example, consider the following use of a TSquare class which does not implement a constructor: o := TSquare.Create; // Calculate its area; a := o.Area; The code calls the area method, but the user forgot to first set the squares width. If the class provided a constructor the user would have to pass a width value if they forget the compiler will quickly remind them. Yet another advantage of using constructors is they can instantiate nested objects for you. Earlier in this article we mentioned that we would prefer to have the TMRUList class use a TStringList to store the items. We said that was awkward because we needed to instantiate the TStgringList class. Well the constructor is the ideal place to do that. When the user instantiates the TMRUList class, its constructor proceeds to instantiate the TStringList
http://www.webtechcorp.co.uk/web-developer-training-delphi-article-oop.htm (26 of 31)30/1/2009 00:42:29

Object Oriented Programming in Delphi - A Guide for Beginners

class. Now, the TStringlist also needs to be freed. When do you want it freed? When the TMRUList itself is freed. This is not automatic, however. When the TMRUList is freed, you need to execute a piece of code which will free the stringlist. Delphis object model provides for this with something called a destructor. A destructor is much like the inverse of a constructor it is called when the object is destroyed. Well look at destructors in a moment lets cover the syntax for constructors first. You declare a constructor in the class declaration, using the keyword Constructor: Type TSquare = Class FX, FY : Integer; FCaption : String; FWidth : Integer; Function Area : Integer; Constructor Create( px, py : Integer; pWidth : Integer; Caption : String; End; Then you write the code for it in the implementation section, again introducing it with the keyword Constructor: Constructor TSquare.Create(px, py : Integer; pWidth : Integer; pCaption : String); Begin Self.FX := px; Self.FY := py; Self.FWidth := pWidth; Self.FCaption := pCaption; End; As you can see, all the constructor is doing is copying the parameters it receives into the instance variables. In this case, Self is optional. However, if I had given the parameters the same names as the instance variables, I would have to use Self on the
http://www.webtechcorp.co.uk/web-developer-training-delphi-article-oop.htm (27 of 31)30/1/2009 00:42:29

Object Oriented Programming in Delphi - A Guide for Beginners

left hand side of the assignment statement to force the compiler to use the instance variable rather than the parameter. The following code shows the constructor for the MRUList class, assuming the class is going to use a StringList called FMList to store the most recently used strings: Constructor TMRUList.Create; Begin FMList := TStringList.Create; End; The constructor simply instantiates the TStringList class and saves it in the instance variable called FMList. As we mentioned already, the class must now free the stringlist when the MRUList class itself is destroyed. You must do this in the classs destructor. Delphi automatically calls a classs destructor when you destroy the object, as in: mruList := TMRUList.Create; Try // Work with mruList here Finally mruList.Free; // This calls the destructor End; When your code calls Free, Delphi automatically calls your classs destructor. You declare your destructor as part of the class declaration using the keyword Destructor. For reasons youll see a little later, Destructors are always called Destroy. You must also declare your destructor as Override youll also see what this means in the next article just believe me if you dont declare your destructor as override it will not be called! Heres the new class declaration showing the constructor, destructor, and the stringList. Type TMruList = Class Private

http://www.webtechcorp.co.uk/web-developer-training-delphi-article-oop.htm (28 of 31)30/1/2009 00:42:29

Object Oriented Programming in Delphi - A Guide for Beginners

FMList : TStringList; Constructor Create; Destructor Destroy; Override; Public Function Count : Integer; Procedure Add( s : String); Function GetString( n : Integer) : String End; To implement the destructor you write code for it in the implementation section much like you do for a constructor. You use the keyword Destroy to introduce the method. The destructor must call a superclass method of the same name, after it has performed its jobs. You do that by using the keyword inherited: Destructor TMRUList.Destroy; Begin FMList.Free; Inherited Destroy; End; Note that I removed the numItems instance variable from the class we can determine that from the stringList itself. Listing 3 shows the reworked code. // Return the number of elements in the MRUList Function TMruList.Count : Integer; Begin Result := Self.FMList.Count; End; // Shift all the elements in the list up by one, // add new element at the start Procedure TMruList.Add(s : String); Var i : Integer; Begin Self.FMList.Insert(0, s); If Self.FMList.Count >= MRUMaxItems Then Self.FMList.Delete( Self.FMList.

http://www.webtechcorp.co.uk/web-developer-training-delphi-article-oop.htm (29 of 31)30/1/2009 00:42:29

Object Oriented Programming in Delphi - A Guide for Beginners

Count 1); End; Function GetString( n : Integer ) : String; Begin If (n >= 0) and (n <= Self. FMList.Count - 1) Then Result := Self.FMList.Strings [n]; End; Listing 3 Third Version of MRU List class using a StringList Note how even though I completely changed the way the class worked, code which uses the class did not change at all because the interface to the class did not change. Thats what encapsulation is all about.

Summary
This was a quick introduction to Object Oriented Programming in Delphi. Theres a lot more to this topic than Ive covered here and subsequent articles will go into more detail. Our 5 Delphi Object Oriented Programming in Delphi course starts from first principles and finishes with creating data aware components which you can integrate into Delphis IDE.

Author Rick Spence is technical director of Web Tech Training and Development, a company with offices in Florida (hyperlink here) and the UK (hyperlink). Web Tech Training and development specialize in developing Web and database applications for other companies (link to consulting web site here), in teaching programmers.

http://www.webtechcorp.co.uk/web-developer-training-delphi-article-oop.htm (30 of 31)30/1/2009 00:42:29

Object Oriented Programming in Delphi - A Guide for Beginners

http://www.webtechcorp.co.uk/web-developer-training-delphi-article-oop.htm (31 of 31)30/1/2009 00:42:29

You might also like