|  | Click Here!

Search the site:
EXPERT SEARCH

| Programming Languages
| Databases
| Security
| Web Services
| Network Services
| Middleware
| Components
| Operating Systems
| User Interfaces
| Groupware & Collaboration
| Content Management
| Productivity Applications
| Hardware
| Fun & Games

EarthWeb sites
Crossnodes
Datamation
Developer.com
DICE
EarthWeb.com
EarthWeb Direct
ERP Hub
Gamelan
GoCertify.com
HTMLGoodies
Intranet Journal
IT Knowledge
IT Library
JavaGoodies
JARS
JavaScripts.com
open source IT
RoadCoders
Y2K Info

| ## Charlie Calvert's C++ Builder Unleashed ## - 20 - ## Encapsulation ### Overview In this chapter, you continue the overview of object-oriented programming begun in the last chapter. In particular, the focus is on encapsulation and properties. The next chapter focuses on polymorphism. This chapter covers the following topics: An overview of encapsulation, including the need to hide data and certain parts of the implementation An in-depth look at the `private`, `protected`, `public`, and __`published` access specifiers Property creation The five basic types of properties Read-only properties Default value declarations for properties This chapter features some of the syntactical jewels in the BCB treasure chest. In particular, it offers a complete array of scoping directives. These tools enable you to fine-tune access to your objects in a way that helps promote their re-use. Properties are also cutting-edge tools, and their implementation in BCB yields some surprising fruits, such as arrays that are indexed on strings. ### Encapsulation The words encapsulation and object are closely linked in my mind. Encapsulation is one of the primary and most fundamental aspects of OOP. It is useful because it helps to enlist related methods and data under a single aegis, and to hide the implementation details that don't need to be exposed or that might change in future versions of an object. The ability to encapsulate methods and data inside an object is important because it helps you design clean, well-written programs. To use a classic example, suppose you were to create an object called `TAirplane`, which would represent (naturally enough) an airplane. This object might have fields such as `Altitude`, `Speed`, and `NumPassengers`; it might have methods such as `TakeOff`, `Land`, `Climb`, and `Descend`. From a design point of view, everything is simpler if you can encapsulate all these fields and methods in a single object, rather than leaving them spread out as individual variables and routines:
    class TAirplane: public TObject

    {

      int Altitude;

      int Speed;

      int NumPassengers;

      void TakeOff();

      void Land();

      void Climb();

      void Descend();

    };
There is a sleek elegance in this simple object declaration. Its purpose and the means for implementing its functionality are readily apparent. Consider the class declaration from the current version of the Object3 program:
     class THierarchy: public TMyObject

    {

      int FTextColor;

      int FBackColor;

    protected:

      virtual void SetTextColor(int Color)

        { FTextColor = Color; textcolor(FTextColor); }

      virtual void SetBackColor(int Color)

        { FBackColor = Color; textbackground(FBackColor); }

    public:

      THierarchy() : TMyObject() {}

      virtual void PrintString(AnsiString S);

      virtual void ClrScr();

      __property int TextColor={read=FTextColor,write=SetTextColor};

      __property int BackColor={read=FBackColor,write=SetBackColor};

    };
`THierarchy` encapsulates a certain amount of functionality, including the `ShowHierarchy` method it inherits from `TMyObject`. If you want to call `ShowHierarchy`, you need to first instantiate an object of type `THierarchy` and then use that object as a qualifier when you call `ShowHierarchy`:
    void main()

    {

      THierarchy *H = new THierarchy();

      H->TextColor = YELLOW;

      H->BackColor = BLUE;

      H->ShowHierarchy(H);

      delete H;

    }
This kind of encapsulation is useful primarily because it makes you treat everything about the `THierarchy` object as a single unit. There are, however, other advantages to this system, which become apparent when you examine the access specifiers used in the class declaration. BCB defines four keywords meant to aid in the process of encapsulation by specifying the access levels for the various parts of an object: `private`: Use this directive to declare a section in a class that can be accessed only from inside the current object. The only way around this limitation is to declare a class as a friend. `protected`: Code declared in a protected section can be accessed only by the current object and by descendant objects. The point here is that protected fields and methods are available primarily to other component developers, not to standard consumers of the object. `public`: Code declared in a public section of an object is available to anyone who uses an object of that particular type. Along with the published section, it is the standard interface of the object. `__published`: Properties that are declared in the published section are public variables that appear in the Object Inspector. Furthermore, you can discover the type of published properties at runtime; simple published properties can be streamed to disk automatically. Because published properties are for use in the Object Inspector, you should not declare a property as published unless the object descends from `TComponent` or one of `TComponent`'s children. `TComponent` is the base object from which all objects that appear on the Component Palette must descend. All the data in your programs should be declared `private` and should be accessed only through methods or properties. As a rule, it is a serious design error to ever give anyone direct access to the data of an object. Giving other objects or non-OOP routines direct access to data is a sure way to get into deep trouble when it comes time to maintain or redesign part of a program. To the degree that it's practical, you might even want the object itself to access its own data primarily through properties or public routines. The whole idea that some parts of an object should remain forever concealed from other programmers is one of the hardest ideas for new OOP programmers to grasp. In fact, in early versions of Turbo Pascal with Objects, this aspect of encapsulation was given short shrift. Experience, however, has shown that many well-constructed objects consist of two parts: Data and implementation sections hidden from the programmers who use the object. A set of interface routines that enable programmers to talk to the concealed methods and data that form the heart of an object. Data and implementation sections are hidden so that the developer of the object can feel free to change those sections at a later date. If you expose a piece of data or a method to the world and find a bug that forces you to change the type of that data or the declaration for that method, you are breaking the code of people who rely on that data or that method. Therefore, it's best to keep all your key methods and data hidden from consumers of your object. Give them access to those methods and procedures only through properties and public methods. Keep the guts of your object private, so that you can rewrite it, debug it, or rearrange it at any time. One way to approach the subject of hiding data and methods is to think of objects as essentially modest beings. An object doesn't want to show the world how it performs some task, and it is especially careful not to directly show the world the data or data structures it uses to store information. The actual data used in an object is a private matter that should never be exposed in public. The methods that manipulate that data should also be hidden from view, because they are the personal business of that object. Of course, an object does not want to be completely hidden from view, so it supplies a set of interface routines that talk to the world--but these interface routines jealously guard the secret of how an object's functionality is actually implemented. A well-made object is like a beautiful woman who conceals her charms from a prying world. Conversely, a poorly made object should also hide, much like an elderly man who doesn't want the world to see how his youth and virility have faded. These analogies are intentionally a bit whimsical, but they help to illustrate the extremely powerful taboo associated with directly exposing data and certain parts of the implementation. Of course, the point of hiding data and implementations is not primarily to conceal how an object works, but to make it possible to completely rewrite the core of an object without changing the way it interacts with the world. In short, the previous analogies collapse when it comes to a functional analysis of data hiding, but they serve well to express the spirit of the enterprise. NOTE: If you have the source code to the VCL, you will find that at least half of most key objects in the BCB code base are declared as `private`. A few complex objects contain several hundred lines of private declarations, and only 20 or 30 methods and properties that serve as an interface for that object. Objects near the top of a hierarchy don't always follow this model, because they usually consist primarily of interface routines. It's the core objects that form the heart of a hierarchy that are currently under discussion. If you will allow me to mix my metaphors rather egregiously, and to introduce a fairly strong set of images, I can provide one further way to think about the internal and public sections of an object. Rather than thinking of the private parts of an object from a sexual perspective, you can literally think of them as the internal organs of the object. As such, they should, by definition, never be exposed directly to the light of day. Instead, they are covered by skin and bone, which is the public interface of the object. Our face and hands are the public part of our being, and our internal organs are covered with skin and bone and never see the light of day. The only reason ever to expose these organs is if you are in the hospital--that is, during object maintenance. Furthermore, any object that does expose its private parts to the light of day is incomplete, a monster from Dr. Moreau's island, where gruesome experiments roam free. ### Simplicity: The Secret of Good Object Design Before moving on, I want to talk about the importance of creating easy-to-use interfaces. Notice, for instance, how easy it is to use the `TTable`, `TDataSource`, and `TDBGrid` objects. These are examples of well-constructed objects with easy-to-use interfaces. If you take these objects away, however, and access the functions in the `DbiProcs.c` directly, you see that `TTable`, `TDBGrid`, and `TDataSource` conceal a great deal of complexity. In other words, if you have to call the raw BDE functions directly to open and view a table, you will find that the process is both tedious and error-prone. Using `TTable`, however, is very easy. You might need to have someone tell you how it works the first time you see it, but after that, the process of getting hooked up to a table and displaying data to the user is trivial. All good programmers should strive to make their objects this easy to use. In the next few chapters, I discuss component creation. Components are very useful because they help guide you in the process of creating a simple, easy-to-use object. If you can drop an object onto a form and hook it up with just a few clicks in the Object Inspector, you know that you have at least begun to create a good design. If you drop an object onto a form and then need to mess around with it for 15 or 20 minutes before you can use it, you know something is probably wrong. Objects should be easy to use, whether they are placed on the Component Palette or not. Simplicity is important because it helps people write error-free programs. If you have to complete 30 steps to hook up an object, there are 30 opportunities to make a mistake. If you need to complete only two steps, it is less likely that an error will occur. It is also easier for people to understand how to complete two steps, whereas they often can become confused when trying to learn a 30-step process. I do not mean to say that the act of creating an object is simple or easy. I have seen great programmers wrestle for months with the overwhelming minutiae of creating a complex object that is easy to use. A good programmer doesn't think the job is done just because the code works. Good code should not only work, but it should also be easy to use! Conversely, I often suspect that an object is poorly designed if it is difficult to use. In most cases, my suspicions turn out to be true, and a complex interface ends up being a wrapper around a buggy and an ineptly coded object. No hard-and-fast rules exist in the area of object design. However, the presence of an easy-to-use interface is a good sign. If you are working with a component, you should be able to drop it onto a form and hook it up in just a few seconds. If you are working with an object that is not encapsulated in a component, you should be able to initialize and use it by writing just a few lines of code. If hooking up takes more than a few minutes, you often have reason to be suspicious of the entire enterprise. The private methods of your objects should also be cleanly and logically designed. However, it is not a disaster if your private methods are difficult to understand, as long as your public methods are easy to use. The bottom line is that the private methods should be designed to accomplish a task, and the public methods should be designed to be easy to use. When you look at the design this way, you can see a vast difference between the public and private methods in an object. The scope of knowledge programmers must master today is huge. Professionals are expected to understand the Windows API, OLE, the Internet, graphics, at least two languages, object design, and database architectures. Some of these fields, such as Internet programming, break down into many complex parts such as ISAPI, CGI, WinINet, TCP/IP, electronic mail, HTTP, and FTP. Learning about all these areas of programming is an enormously complex task. In fact, for all practical purposes, it is impossible. No one person can know everything necessary to write most contemporary programs from scratch. As a result, he or she needs to find easy-to-use objects that encapsulate complexity and make it usable. The worst mistake an object creator can make is to insist that the consumer of the object knows as much about the subject as its creator. It's not enough to define a set of useful routines that can speed development if you already know a subject inside and out. Instead, you need to create objects that someone can use, even if he or she doesn't understand the details of how a particular subject works. The trick to good object design, of course, is to simultaneously present an easy-to-use interface, while also giving experienced programmers access to the fine points of a particular area. Once again, `TTable` and `TQuery` serve as an example. `TTable` is an easy-to-use object providing neophytes easy access to data. At the same time, it allows an expert to manipulate one-to-many relationships, lookups, filters, and indices. Add `TQuery` to the mix, and there are few limits to what you can do with these objects in terms of manipulating databases. `TMediaPlayer` is another example of a component that takes a relatively complex API and makes it easy to use. It is arguable that this component could have a bit more depth, but it allows programmers to run multimedia applications without having to understand `mciSendCommand` and its many complex parameters, structures and constants. The bottom line is simplicity. If you create an object that is as hard to use as the API it encapsulates, 95 percent of the people who see your object will consider it useless. The reason programmers create objects is to provide an easy, bug-free interface to a particular area of programming. As you read through the next few chapters, you might consider thinking about the importance of creating a simple interface to your objects. Having a simple interface is particularly important for the top-level objects in your hierarchy. ### A Concrete Example Neither `TMyObject` nor `THierarchy` provides much scope for exploring encapsulation. As a result, it's time to introduce a new class called `TWidget`, which is a descendant of `TCustomControl` and uses a descendant of `THierarchy` through aggregation. In Chapter 21, "Polymorphism," `TWidget` becomes the ancestor of a series of different kinds of widgets that can be stored in a warehouse. In other words, in a computer factory `TWidget` might be the ancestor of several classes of objects such as `TSiliconChip`, `TAddOnBoard`, and `TPowerSupply`. Descendants of these objects might be `TPentiumChip`, `TPentiumProChip`, `TVideoBoard`, and so on. NOTE: I suppose one could literally think of the `TWidget` descendants as representations of concrete objects or as abstract representations of these objects. The meaning I intend is the latter. This later, more abstract, technique is similar to the process used in object-oriented databases. Instead of having a table that stores rows of raw data, the table could instead store objects that represent data. When a new 486 chip rolls off the line, a new `T486Chip` object is added to the database to represent this physical object. You could then query the abstract object and ask it questions: "When were you made? What part of the warehouse are you stored in? What is your wholesale price? What is your street price?" Thus, the abstract object would be a computerized representation of something in the real world. Creating computerized representations of physical objects is a very common use of OOP, but it is not the primary reason for the existence of this technology. The great benefits of OOP are related to the reuse, design, and maintenance of software. Using OOP to represent real-world objects is only one branch of this field of knowledge. Note that object hierarchies always move from the general to the specific:
    TWidget

    TSiliconChip

    TPentiumChip
The underlying logic is simple enough: 1. A `TWidget` could be almost any object that is bought and sold. 2. A `TSiliconChip` is some kind of silicon-based entity. 3. A `TPentiumChip` is a specific kind of computer chip that has a real-world counterpart. The movement is from the abstract to the specific. It is almost always a mistake to embody specific traits of an object in a base class for a hierarchy. Instead, these early building blocks should be so broad in scope that they can serve as parents to a wide variety of related objects or tools. The main reason for this rule might not become apparent to all readers until they have seen Chapter 21. The condensed explanation, however, looks like this: `TWidget` encapsulates certain basic functionality that applies to all widgets. For instance, all widgets might keep track of the date they were created, whereas only certain types of widgets might support the MX instruction set. Therefore, you can use polymorphism to ask all your widgets certain basic questions, such as when they were created. Conversely, there are certain types of questions you can ask only of widgets that are also computer processors. This system would not work if you did not move from the general to the specific. In short, you want all widgets to have certain basic functionality, and then as you move up the hierarchy, you can provide specific domains of knowledge or functionality to particular types of objects. The Widget1 program found on the CD that accompanies this book includes a declaration for class `TWidget`. (See Listing 20.1.) This declaration appears in a file called `Widgets.h`. `TWidget` descends from the VCL class called `TCustomControl` and uses a descendant of `THierarchy` through aggregation. As a result, you will need to add both `MyObject` and the new Widgets files to your projects. I have created a new file called Widgets because I am building a new type of object that is distinct from `THierarchy` and `TMyObject`. I could, of course, have put all the objects in one file, but I thought it made more sense to separate the different types of objects into different files. There is no hard-and-fast rule governing this kind of decision, and you can take whichever course makes sense to you on a case-by-case basis. I have also added a new class to `TMyObject`:
    class TListHierarchy: public TMyObject

    {

    private:

      TStringList *FList;

    protected:

      virtual void PrintString(AnsiString S)

        { FList->Add(S); }

    public:

      TListHierarchy() { FList = new TStringList(); }

      __fastcall virtual ~TListHierarchy() { delete FList; }

      TStringList *GetHierarchy(TObject *AnObject)

        { ShowHierarchy(AnObject); return FList; }

    };
As you can see, this class stores its information in a `TStringList`. You can retrieve the list from the class by using the `GetHierarchy` method:
    ListBox1->Items = MyWidget->GetHierarchy();
After you make this call, the list box referenced in the code would contain an object hierarchy. #### TWidget and Its Destructor The Widgets unit is very simple at this point. To create it, I went to the files menu and chose New Unit. I then saved the file as `Widgets.cpp` and edited the header so that it looked like this:
    ///////////////////////////////////////

    // Widgets.h

    // Learning how to use objects

    // Copyright (c) 1997 by Charlie Calvert

    //

    #ifndef WidgetsH

    #define WidgetsH

    #include "myobject.h"

    class TWidget: public TCustomControl

    {

    private:

      TListHierarchy *Hierarchy;

    public:

      virtual __fastcall TWidget(TComponent *AOwner): TCustomControl(AOwner)

        { Hierarchy = new TListHierarchy; }

      virtual __fastcall ~TWidget()

        { delete Hierarchy; }

      TStringList *GetHierarchy()

        { return Hierarchy->GetHierarchy(this); }

    };

    #endif
This object uses aggregation to enlist the functionality of the `TListHierarchy` object for its own purposes. In particular, it declares the object as private data and then allocates memory for it in its constructor, and deallocates memory in its destructor:
    virtual __fastcall ~TWidget()

        { delete Hierarchy; }
NOTE: A destructor is declared by writing the name of the object with a tilde prefaced to it: `~TWidget`. You rarely have reason to call a destructor explicitly. Instead, a destructor is called automatically when you use the `delete` operator on the entire object, when you call Free, or when a local object goes out of scope:

    delete MyWidget; // Automatically calls the destructor

Destructors exist so you can have a place to deallocate any memory associated with an object, or to perform any other cleanup chores. The destructor exists for your convenience and serves no other purpose than to give you a chance to clean up before your object shuts down.
All VCL objects inherit a virtual destructor declared in `TObject`. If you create a destructor for one of your own VCL objects, it must have the same signature used by the destructor in the `TWidget` object. That is, it must be declared as virtual `__fastcall`. This is because of the precedent set by the destructor found in `TObject`.

The `GetHierarchy` method of `TWidget` returns the result of a call to the `Hierarchy::GetHierarchy` method:

    TStringList *GetHierarchy()

        { return Hierarchy->GetHierarchy(this); }

As you can see, this function hardcodes a reference to the `TWidget` object into this call. You therefore can't use this version of the `Widget` object to get the hierarchy of another object, but only for retrieving its own hierarchy.

To make sure you understand how I got started with this new series of files, I have created a very simple program called Widget1 that contains the `Widgets` unit and a main program that uses it. The code for the program is available on this book's CD, and the output from the program is shown in Figure 20.1.
FIGURE 20.1. The output from the Widget1 program found on the CD that accompanies this book.

The main program contains one procedure that looks like this:

    void __fastcall TForm1::ShowHierarchyBtnClick(TObject *Sender)

    {

      Memo1->Clear();

      TWidget *W = new TWidget(Memo1);

      Memo1->Lines = W->GetHierarchy();

      delete W;

    }

If you have questions on how to create this program, go to the `Widget1` directory and study the example provided there. After you are set up, you can read the note below and then copy the `Widgets.cpp` and `Widgets.h` files to the `Utils` directory, where `CodeBox` is kept. This file is used in `Widgets2`, which is described in the next section of this chapter.

> NOTE: Actually, you might want to be careful copying the files. If you are following along by creating copies of the files by hand, then go ahead and copy the files. If you are working from my source, be careful that you don't overwrite my copy of the file when you move the file to the `Utils` directory.

#### Working with Widgets

In the last section, you got started with the `TWidget` object. In the Widget2 program, shown in Listings 20.1 through 20.5, I add data and methods to the object so it can serve as the base class for an object that represents some kind of widget such as a computer chip, a light bulb, a book, or whatever. Because the object could have such a wide range of uses, I keep it very abstract, giving it only a few traits such as a cost, a creation time, the ability to draw itself, and the ability to stream itself.

> NOTE: You will find that the Widgets module gets rewritten several times over the course of the next few chapters. As a result, the version of this unit used in the Widget2 program is stored in the `Widgets2` directory on the CD that accompanies this book. There is a second version of this unit, stored in the `Utils` subdirectory, that contains the final version of `Widgets.cpp`.
> If you are working along with this text, creating your own version of the program, it might be best to copy `Widgets.cpp` out to the `Utils` directory and update it little by little as the next few chapters unfold. If your efforts get fouled for one reason or another, you can always revert to the versions stored on the CD.

Listing 20.1. The Main unit for the Widget2 program.

    ///////////////////////////////////////

    // Main.cpp

    // Learning about objects

    // Copyright (c) 1997 by Charlie Calvert

    //

    #include <vcl\vcl.h>

    #pragma hdrstop

    #include "widgets.h"

    #include "Main.h"

    #pragma resource "*.dfm"

    TForm1 *Form1;

    __fastcall TForm1::TForm1(TComponent* Owner)

      : TForm(Owner)

    {

    }

    void __fastcall TForm1::CreateBtnClick(TObject *Sender)

    {

      TWidget * Widget= new TWidget(this);

      Widget->Left = ClientWidth / 4 - Widget->Width / 2;

      Widget->Top = (ClientHeight / 2) + Widget->Height;

      Widget->Parent = this;

      Widget->Cost = 3.3;

      Widget->Description = "This is a widget";

      Widget->TimeCreated = Now();

      Memo1->Lines = Widget->GetHierarchy();

      Widget->Show();

      Edit1->Text = Widget->TimeCreated;

      WriteWidgetToStream("Afile.dat", Widget);

    }

    void __fastcall TForm1::ReadFromStreamBtnClick(TObject *Sender)

    {

      TWidget *Widget = ReadWidgetFromStream("AFile.dat");

      Widget->Parent = this;

      Memo1->Lines = Widget->GetHierarchy();

      Widget->Show();

      Edit1->Text = Widget->TimeCreated;

    }

    void __fastcall TForm1::FormResize(TObject *Sender)

    {

      Memo1->Left = ClientWidth / 2;

    }

Listing 20.2. The core of the Widget2 program is the Widgets unit. The header for that module is shown here.

    ///////////////////////////////////////

    // Widgets.h

    // Learning how to use objects

    // Copyright (c) 1997 by Charlie Calvert

    //

    #ifndef WidgetsH

    #define WidgetsH

    #include "myobject.h"

    class __declspec(delphiclass) TWidget;

    namespace Widgets

    {

      void __fastcall Register();

    }

    TWidget *ReadWidgetFromStream(AnsiString StreamName);

    void WriteWidgetToStream(AnsiString StreamName, TWidget *Widget);

    class TWidget: public TCustomControl

    {

    private:

      TListHierarchy *Hierarchy;

      Currency FCost;

      TDateTime FTimeCreated;

      AnsiString FDescription;

      void __fastcall SetTimeCreated(AnsiString S);

      AnsiString __fastcall GetTimeCreated();

    protected:

      virtual void __fastcall Paint(void);

    public:

      __fastcall virtual TWidget(TComponent *AOwner): TCustomControl(AOwner)

        { Hierarchy = new TListHierarchy(); Width = 25; Height = 25; }

      __fastcall virtual TWidget(TComponent *AOwner, int ACol, int ARow);

      __fastcall virtual ~TWidget() { delete Hierarchy; }

      virtual AnsiString GetName() { return "Widgets"; }

      TStringList *GetHierarchy() { return Hierarchy->GetHierarchy(this); }

      void __fastcall WidgetMouseDown(TObject *Sender, TMouseButton Button,

        TShiftState Shift, int X, int Y);

    __published:

      __property Currency Cost={read=FCost, write=FCost};

      __property AnsiString TimeCreated={read=GetTimeCreated, write=SetTimeCreated};

      __property AnsiString Description={read=FDescription, write=FDescription};

    };

    #endif

Listing 20.3. The main file for the Widgets units.

    ///////////////////////////////////////

    // Widgets.cpp

    // Learning how to use objects

    // Copyright (c) 1997 by Charlie Calvert

    //

    #include <vcl\vcl.h>

    #include <conio.h>

    #pragma hdrstop

    #include "widgets.h"

    #pragma link "myobject.obj"

    void WriteWidgetToStream(AnsiString StreamName, TWidget *Widget)

    {

      TFileStream *Stream = new TFileStream(StreamName, fmCreate | fmOpenRead);

      Stream->WriteComponent(Widget);

      delete Stream;

    }

    TWidget *ReadWidgetFromStream(AnsiString StreamName)

    {

      Widgets::Register();

      TFileStream *Stream = new TFileStream(StreamName, fmOpenRead);

      TWidget *Widget = (TWidget *)Stream->ReadComponent(NULL);

      delete Stream;

      return Widget;

    }

    __fastcall TWidget::TWidget(TComponent *AOwner, int ACol, int ARow)

      : TCustomControl(AOwner)

    {

      Hierarchy = new TListHierarchy();

      Left = ACol;

      Top = ARow;

      Width = 25;

      Height = 25;

      OnMouseDown = WidgetMouseDown;

    }

    AnsiString __fastcall TWidget::GetTimeCreated()

    {

      return FTimeCreated.DateTimeString();

    }

    void __fastcall TWidget::SetTimeCreated(AnsiString S)

    {

      FTimeCreated = TDateTime(S);

    }

    void__fastcall TWidget::Paint()

    {

      Canvas->Brush->Color = clBlue;

      Canvas->Rectangle(0, 0, ClientWidth, ClientHeight);

      Canvas->Brush->Color = clYellow;

      Canvas->Ellipse(0, 0, ClientWidth, ClientHeight);

    }

    void __fastcall TWidget::WidgetMouseDown(TObject *Sender, TMouseButton Button,

        TShiftState Shift, int X, int Y)

    {

      ShowMessage(Format("%m", OPENARRAY(TVarRec, (FCost))));

    }

    namespace Widgets

    {

      void __fastcall Register()

      {

        TComponentClass classes[1] = {__classid(TWidget)};

        RegisterClasses(classes, 0);

      }

    }

Listing 20.4. The header file for MyObjects with the new TListHiearchy object added to it.

    ///////////////////////////////////////

    // MyObject.h

    // Learning how to use objects

    // Copyright (c) 1997 by Charlie Calvert

    //

    #ifndef MyObjectH

    #define MyObjectH

    #include <conio.h>

    #include <vcl\stdctrls.hpp>

    class TMyObject :public TObject

    {

    protected:

      virtual void PrintString(AnsiString S);

    public:

      TMyObject() : TObject()  {}

      void ShowHierarchy(TObject *AnObject);

    };

    class TListHierarchy: public TMyObject

    {

    private:

      TStringList *FList;

    protected:

      virtual void PrintString(AnsiString S)

        { FList->Add(S); }

    public:

      TListHierarchy() { FList = new TStringList(); }

      __fastcall virtual ~TListHierarchy() { delete FList; }

      TStringList *GetHierarchy(TObject *AnObject)

        { ShowHierarchy(AnObject); return FList; }

    };

    class __declspec(delphiclass) TVCLHierarchy;

    class THierarchy: public TMyObject

    {

      friend TVCLHierarchy;

      int FTextColor;

      int FBackColor;

    protected:

      virtual void SetTextColor(int Color)

        { FTextColor = Color; textcolor(FTextColor); }

      virtual void SetBackColor(int Color)

        { FBackColor = Color; textbackground(FBackColor); }

    public:

      THierarchy() : TMyObject() {}

      virtual void PrintString(AnsiString S);

      virtual void ClrScr();

      __property int TextColor={read=FTextColor,write=SetTextColor};

      __property int BackColor={read=FBackColor,write=SetBackColor};

    };

    class TVCLHierarchy : public THierarchy

    {

      TMemo *FMemo;

    protected:

      virtual void SetTextColor(int Color)

        { FTextColor = Color; FMemo->Font->Color = TColor(FTextColor); }

      virtual void SetBackColor(int Color)

        { FBackColor = Color; FMemo->Color = TColor(FBackColor); }

    public:

      TVCLHierarchy(TMemo *AMemo): THierarchy() { FMemo = AMemo; }

      virtual void PrintString(AnsiString S)

        { FMemo->Lines->Add(S); }

      virtual void ClrScr() { FMemo->Clear(); }

    };

    #endif

Listing 20.5. The main file of the MyObject module.

    ///////////////////////////////////////

    // MyObject.cpp

    // Learning how to use objects

    // Copyright (c) 1997 by Charlie Calvert

    //

    #include <vcl\vcl.h>

    #include <conio.h>

    #pragma hdrstop

    #include "myobject.h"

    void TMyObject::PrintString(AnsiString S)

    {

      printf("%s\n", S.c_str());

    }

    void TMyObject::ShowHierarchy(TObject *AnObject)

    {

      TClass AClass;

      AnsiString AClassName = AnsiString(AnObject->ClassName()).c_str();

      PrintString(AClassName);

      AClass = AnObject->ClassParent();

      while (True)

      {

        AClassName = AnsiString(AClass->ClassName());

        PrintString(AClassName);

        if (AClassName == "TObject")

          break;

        AClass = AClass->ClassParent();

      }

    }

    void THierarchy::PrintString(AnsiString S)

    {

      char Temp[250];

      sprintf(Temp, "%s\n\r", S.c_str());

      cputs(Temp);

    }

    void THierarchy::ClrScr()

    {

      clrscr();

    }

The functionality associated with this program is still severely limited. An object of type `TWidget` is created, and its hierarchy is shown. The program assigns a price to the widget, and a bare representation of a widget is displayed on the screen. The program also saves and loads the `Widget` object to disk.

The output from this program is shown in Figure 20.2.
FIGURE 20.2. The output from the Widget2 program.

From a user's point of view, this is pretty tame stuff. However, the declaration for class `TWidget` shows programmers a good deal about how BCB implements encapsulation:

    class TWidget: public TCustomControl

    {

    private:

      TListHierarchy *Hierarchy;

      Currency FCost;

      TDateTime FTimeCreated;

      AnsiString FDescription;

      void __fastcall SetTimeCreated(AnsiString S);

      AnsiString __fastcall GetTimeCreated();

    protected:

      virtual void __fastcall Paint(void);

    public:

      __fastcall virtual TWidget(TComponent *AOwner): TCustomControl(AOwner)

        { Hierarchy = new TListHierarchy(); Width = 25; Height = 25; }

      __fastcall virtual TWidget(TComponent *AOwner, int ACol, int ARow);

      __fastcall virtual ~TWidget() { delete Hierarchy; }

      virtual AnsiString GetName() { return "Widgets"; }

      TStringList *GetHierarchy() { return Hierarchy->GetHierarchy(this); }

      void __fastcall WidgetMouseDown(TObject *Sender, TMouseButton Button,

        TShiftState Shift, int X, int Y);

    __published:

      __property Currency Cost={read=FCost, write=FCost};

      __property AnsiString TimeCreated={read=GetTimeCreated, write=SetTimeCreated};

      __property AnsiString Description={read=FDescription, write=FDescription};

    };

The private section of `TWidget` contains several fields of data and two methods:

    private:

      TListHierarchy *Hierarchy;

      Currency FCost;

      TDateTime FTimeCreated;

      AnsiString FDescription;

      void __fastcall SetTimeCreated(AnsiString S);

      AnsiString __fastcall GetTimeCreated();

All of the private data in the program has variable names that begin with the letter F. As stated before, this is a convention and not a syntactical necessity. These variables are called internal storage, or data stores.

Internal storage should always be declared `private`, and as such cannot be accessed from outside of this unit. Other objects should never access any of this data directly, but should manipulate it through a predefined interface that appears in the `protected`, `published`, or `public` sections. The F in these variable names stands for field. If you want, however, you can think of the F in these names as standing for forbidden, as in "it is forbidden to directly access this data!"

Don't step between a mother grizzly bear and her cubs. Don't ask who's buried in Grant's tomb during the middle of a job interview. Don't swim with the sharks if you are bleeding. Don't declare public data in a production-quality program! The problem is not that the error is embarrassing, but that it is going to cause you grief!

The `GetTimeCreated` and `SetTimeCreated` functions are also declared `private`, and you will see that they are accessed through a property. Most objects have many more private methods, but `TWidget` is relatively bare in this department. The lack of private methods occurs because `TWidget` is such a simple object that there isn't much need to perform complex manipulations of its data.

The `protected` section is simple and contains a single virtual method called `Paint`. This portion of the object can be accessed by descendants of `TWidget`, but not by an instance of the class. For example, you will have trouble if you write the following code:

    {

      TWidget *Widget = new TWidget(this);

      Widget->Paint(); // this line won't compile

      delete Widget();

    }

Only a descendant of `TWidget` can explicitly call the `Paint` method.

The methods in the `public` section of the object make it possible to manipulate the widgets that you declare:

    public:

      __fastcall virtual TWidget(TComponent *AOwner): TCustomControl(AOwner)

        { Hierarchy = new TListHierarchy(); Width = 25; Height = 25; }

      __fastcall virtual TWidget(TComponent *AOwner, int ACol, int ARow);

      __fastcall virtual ~TWidget() { delete Hierarchy; }

      TStringList *GetHierarchy() { return Hierarchy->GetHierarchy(this); }

      void __fastcall WidgetMouseDown(TObject *Sender, TMouseButton Button,

        TShiftState Shift, int X, int Y);

Here, you can see several constructors, a destructor, a routine that lets you iterate through the hierarchy of the object, and a routine that handles mouse clicks on the object. All of these are common activities and need to be declared `public`.

The first constructor for the object sets up the aggregated `THierarchy` object and then sets the object's width and height. The second constructor allows you to establish the `Left` and `Top` properties for the object.

You have now had an overview of all the code in the Widget program, except for its published properties, which will be discussed in the next section. The discussion so far has concentrated on the BCB object-scoping directives. You have learned about the `private`, `protected`, `public`, and `published` sections of a program, and have seen why each is necessary.

### Properties

Properties provide several advantages:

- Properties enable you to hide data and implementations.

- If you write a component and place it in the Component Palette, its published properties appear in the Object Inspector.

- Some properties can be made available at design-time, and variables are available at runtime only.

- Properties can have side effects such as not only setting the value of the `FWidth` variable, but also physically changing the width of the object that appears on the screen.

- Property access methods can be declared `virtual`, which gives them more flexibility than simple variables.

The Widget2 program contains three properties, as shown here:

    __published:

      __property Currency Cost={read=FCost, write=FCost};

      __property AnsiString TimeCreated={read=GetTimeCreated, write=SetTimeCreated};

      __property AnsiString Description={read=FDescription, write=FDescription};

Because the `TWidget` class is a descendant of `TComponent`, all these properties can be put in the `published` section, and therefore could be seen from inside the Object Inspector if the object were compiled into BCB's library. It usually does not make sense to create published sections in objects that do not have `TComponent` in their ancestry.

Remember that properties in the published section have the advantages, and the overhead, associated with a heavy dose of runtime type information. In particular, properties placed in the public section will automatically be streamed to disk!

There is no rule that says which properties should be declared in the `published` or `public` sections. In fact, properties often appear in public sections, although there is little reason for them to be in `private` or `protected` sections.

The cost and description properties shown here are simple tools that do nothing more than hide data and lay the groundwork for their use inside the Object Inspector.

    property Currency Cost={read=FCost, write=FCost};

The declaration starts with the keyword `property`, which performs the same type of syntactical chore as class or struct. Every property must be declared as having a certain type, which in this case is `Currency`.

Most properties can be both read and written. The `read` directive for the `Cost` property states that the value to be displayed is `FCost` and the value to write is `FCost`. In short, writing

    {

      Widget->Cost = 2;

      int i = Widget.Cost

    }

sets `FCost` to the value `2` and sets `i` to the value of `FCost` (again, `2`).

The reasons for doing this are twofold:

- To hide data so that it is protected.

- To create a syntax that allows properties to be shown in the Object Inspector. Of course, you won't see these values in the Object Inspector until you metamorphose the object into a component, which is a subject that will be covered in Chapter 22, "Creating Descendants of Existing Components."

The `Cost` and `Description` properties provide what is called direct access; they map directly to the internal storage field. The runtime performance of accessing data through a direct-access property is exactly the same as accessing the private field directly.

The `Cost` and `Description` examples represent the simplest possible case for a property declaration. The `TimeCreated` property presents a few variations on these themes:

    property AnsiString TimeCreated={read=GetTimeCreated, write=SetTimeCreated};

Rather than reading a variable directly, `TimeCreated` returns the result of a private function:

    AnsiString __fastcall TWidget::GetTimeCreated()

    {

      return FTimeCreated.DateTimeString();

    }

`SetQuantity`, on the other hand, enables you to change the value of the `FQuantity` variable:

    void __fastcall TWidget::SetTimeCreated(AnsiString S)

    {

      FTimeCreated = TDateTime(S);

    }

`GetTimeCreated` and `SetTimeCreated` are examples of access methods. Just as the internal storage for direct access variables begins by convention with the letter F, access methods usually begin with either `Set` or `Get`.

Take a moment to consider what is happening here. To use the `Quantity` property, you need to use the following syntax:

    {

      AnsiString S;

      W->TimeCreated = "1/1/56 02:53:35 AM";

      S = W->TimeCreated;

    }

Note that when you are writing to the `FTimeCreated` variable, you don't write

    W->TimeCreated(Now());

Instead, you use the simple, explicit syntax of a direct assignment:

    W->TimeCreated = Now();

BCB automatically translates the assignment into a function call that takes a parameter. C++ buffs will recognize this as a limited form of operator overloading.

If there were no properties, the previous code would look like this:

    {

      AnsiString S;

      W->SetTimeCreated(Now());

      S = W->GetTimeCreated;

    }

Instead of remembering one property name, this second technique requires you to remember two, and instead of the simple assignment syntax, you must remember to pass a parameter. Although it is not the main purpose of properties, it should now be obvious that one of their benefits is that they provide a clean, easy-to-use syntax. Furthermore, they allow you to completely hide the implementation of your `Get` and `Set` methods if you so desire.

### Streaming Classes

Published properties allow you to automatically stream the data of your program. In particular, most published properties will be automatically written to your DFM files and restored when they are reloaded.

The following code shows how to explicitly write a component to disk:

    void WriteWidgetToStream(AnsiString StreamName, TWidget *Widget)

    {

      TFileStream *Stream = new TFileStream(StreamName, fmCreate | fmOpenWrite);

      Stream->WriteComponent(Widget);

      delete Stream;

    }

To call this method, you might write code that looks like this:

    TWidget * Widget= new TWidget(this);

    Widget->Parent = this;

    Widget->Left = 10;

    Widget->Top = 10;

    Widget->Cost = 3.3;

    Widget->Description = "This is a widget";

    Widget->TimeCreated = Now();

    WriteWidgetToStream("Afile.dat", Widget);

This code creates an instance of the `Widget` component, assigns values to its data, and then writes it to disk in the last line of the code quoted here. This creates a persistent version of the object and explicitly preserves each property value.

The `TFileStream` component can be used to stream anything to disk; however, it has a very useful `WriteComponent` method that will stream an object automatically, taking care to store the current values of most published properties. In some cases, you might find a property that the VCL does not know how to stream. You can usually convert the property to an AnsiString, which the VCL component will know how to stream. This is what I did in this example, when I found that the VCL didn't want to write a variable of type `TDateTime`.

To construct an instance of `TFileStream`, you pass in the name of the file you want to work with and one or more flags specifying the rights you want when you open the file. These flags are listed in the online help and declared in `SysUtils.hpp`:

    #define fmOpenRead (Byte)(0)

    #define fmOpenWrite (Byte)(1)

    #define fmOpenReadWrite (Byte)(2)

    #define fmShareCompat (Byte)(0)

    #define fmShareExclusive (Byte)(16)

    #define fmShareDenyWrite (Byte)(32)

    #define fmShareDenyRead (Byte)(48)

    #define fmShareDenyNone (Byte)(64)

    #define fmClosed (int)(55216)

    #define fmInput (int)(55217)

    #define fmOutput (int)(55218)

    #define fmInOut (int)(55219)

FileStreams have a `Handle` property that you can use if you need it for special file operations or if you want to pass it to a handle-based C library file IO routine.

This is not the place to go into a detailed description of how streaming works in the VCL. However, you might want to open up `Classes.hpp` and take a look at the `TReader` and `TWriter` classes, which are helper objects that the VCL uses when it is time to stream an object. These classes have methods such as `ReadInteger`, `WriteInteger`, `ReadString`, `WriteString`, `ReadFloat`, and `WriteFloat`. `TReader` and `TWriter` are for use by the VCL, but I have used these classes for my own purposes on several occasions.

Here is how to read a component from a stream:

    namespace Widgets

    {

      void __fastcall Register()

      {

        TComponentClass classes[1] = {__classid(TWidget)};

        RegisterClasses(classes, 0);

      }

    }

    TWidget *ReadWidgetFromStream(AnsiString StreamName)

    {

      Widgets::Register();

      TFileStream *Stream = new TFileStream(StreamName, fmOpenRead);

      TWidget *Widget = (TWidget *)Stream->ReadComponent(NULL);

      delete Stream;

      return Widget;

    }

This code first registers the `TWidget` type with the system. This is necessary because the VCL needs to know what type of object you want to stream. Of course, when working with components that are placed on the Component Palette, you can be sure the system has already registered the object for you. However, if you did not drop a component on a form but created it by hand, you might have to register the component before you can stream it.

The `Register` method needs to appear in its own namespace because there will be many register functions in a typical application--at least one for each component. Most of the time this function will be called automatically by the compiler, and it is a bit unusual for you to have to call it explicitly.

Notice that the compiler will automatically construct an object for you if you pass in `NULL` when calling `ReadComponent`:

    TWidget *Widget = (TWidget *)Stream->ReadComponent(NULL);

Alternatively, you can create the component yourself and then pass it to `ReadComponent` so that its properties will be lifted from the stream.

In the last few pages, you had a good look at the Widget2 program. There are several additional traits of properties that should be explored, however, before moving on to the colorful warehouse simulation found in the next chapter.

### More on Properties

BCB provides support for five different types of properties:

- Simple properties are declared to be integers, characters, or strings.

- Enumerated properties are declared to be of some enumerated type. When shown in the Object Inspector, you can view them with a drop-down list.

- Set properties are declared to be of type `Set`. `BorderIcons` from `TForm` is an example of this type of property. You can choose only one enumerated value at a time, but you can combine several values in a property of type `Set`.

- Object properties are declared to be of some object type, such as the `Items` property from the `TListBox` component, which is declared to be of type `TStrings`.

- Array properties are like standard arrays, but you can index on any type, even a string. The classic example of this kind of property is the `Strings` property in a `TStringList`.

The PropertyTest program (in Listing 20.6) gives an example of each of the five types of properties. It also gives the `TStringList` object a fairly decent workout. The program itself is only minimally useful outside the range of a purely academic setting such as this book.
Listing 20.6. The main unit for the PropertyTest program.

    ///////////////////////////////////////

    // Main.cpp

    // Learning about properties

    // Copyright (c) 1997 by Charlie Calvert

    //

    #include <vcl\vcl.h>

    #pragma hdrstop

    #include "Main.h"

    #include "propertyobject1.h"

    #pragma resource "*.dfm"

    TForm2 *Form2;

    //--------------------------------------------------------------------------

    __fastcall TForm2::TForm2(TComponent* Owner)

    : TForm(Owner)

    {

    }

    //--------------------------------------------------------------------------

    void __fastcall TForm2::bCreateObjectsClick(TObject *Sender)

    {

      TMyProps *M;

      char Ch;

      int i;

      M = new TMyProps(this);

      M->Parent = this;

      M->SimpleProp = 25;

      M->EnumProp = teEnum;

      M->SetProp = TSetProp() << teEnum << teSet;

      M->StrArrayProp["Jones"] = "Sam, Mary";

      M->StrArrayProp["Doe"] = "John, Johanna";

      ListBox1->Items->Add(M->StrArrayProp["Doe"]);

      ListBox1->Items->Add(M->StrArrayProp["Jones"]);

      for (i = 0; i < M->ObjectProp->Count; i++)

        ListBox2->Items->Add(M->ArrayProp[i]);

      Ch = M->Default1;

      ListBox1->Items->Add(Ch);

    }

Listing 20.7. The header file for the PropertyObject unit.

    ///////////////////////////////////////

    // PropertyObject.h

    // Learning about properties

    // Copyright (c) 1997 by Charlie Calvert

    //

    #ifndef PropertyObject1H

    #define PropertyObject1H

    enum TEnumType {teSimple, teEnum, teSet, teObject, teArray};

    typedef Set<TEnumType, teSimple, teArray> TSetProp;

      class TCouple: public TObject

      {

      public:

        AnsiString Husband;

        AnsiString Wife;

        TCouple() {}

      };

      class TMyProps: public TCustomControl

      {

      private:

        int FSimple;

        TEnumType FEnumType;

        TSetProp FSetProp;

        TStringList *FObjectProp;

        char FDefault1;

        AnsiString __fastcall GetArray(int Index);

        AnsiString __fastcall GetStrArray(AnsiString S);

        void SetStrArray(AnsiString Index, AnsiString S);

      protected:

        void virtual __fastcall Paint();

      public:

        virtual __fastcall TMyProps(TComponent *AOwner);

        virtual __fastcall ~TMyProps();

        __property AnsiString ArrayProp[int i]={read=GetArray};

        __property AnsiString StrArrayProp[AnsiString i]=

          {read=GetStrArray,write=SetStrArray};

      __published:

        __property int SimpleProp={read=FSimple, write=FSimple};

        __property TEnumType EnumProp={read=FEnumType, write=FEnumType};

        __property TSetProp SetProp={read=FSetProp, write=FSetProp};

        __property TStringList *ObjectProp={read=FObjectProp, write=FObjectProp};

        __property char Default1={read=FDefault1, write=FDefault1, default= `1'};

      };

    #endif

Listing 20.8. The source for the PropertyTest unit shows how to work with properties.

    ///////////////////////////////////////

    // PropertyObject.cpp

    // Learning about properties

    // Copyright (c) 1997 by Charlie Calvert

    //

    #include <vcl\vcl.h>

    #pragma hdrstop

    #include "PropertyObject1.h"

    #include "codebox.h"

    __fastcall TMyProps::TMyProps(TComponent *AOwner)

     :TCustomControl(AOwner)

    {

      Width = 100;

      Height = 100;

      Left = (((TForm*)(AOwner))->ClientWidth / 2) - (Width / 2);

      Top = (((TForm*)(AOwner))->ClientHeight / 2) - (Height / 2);

      FObjectProp = new TStringList();

      Default1 = `1';

    };

    __fastcall TMyProps::~TMyProps()

    {

      int i;

      for (i = 0; i < FObjectProp->Count; i++)

      {

        FObjectProp->Objects[i]->Free();

      }

      FObjectProp->Free();

    }

    void __fastcall TMyProps::Paint()

    {

      Canvas->Brush->Color = clBlue;

      TCustomControl::Paint();

      Canvas->Rectangle(0, 0, Width, Height);

      Canvas->TextOut(1, 1, "FSimple: " + IntToStr(FSimple));

      Canvas->TextOut(1, Canvas->TextHeight("Blaise"), GetArray(0));

      Canvas->TextOut(1, Canvas->TextHeight("Blaise") * 2, FObjectProp->Strings[1]);

    };

    AnsiString __fastcall TMyProps::GetArray(int Index)

    {

      return FObjectProp->Strings[Index];

    }

    AnsiString __fastcall TMyProps::GetStrArray(AnsiString S)

    {

      TCouple *Couple;

      Couple = (TCouple*)(FObjectProp->Objects[FObjectProp->IndexOf(S)]);

      return Couple->Husband + ", " + Couple->Wife;

    }

    AnsiString GetHusband(AnsiString S)

    {

      return StripLastToken(S, `,');

    }

    AnsiString GetWife(AnsiString S)

    {

      return StripFirstToken(S, `,');

    }

    void TMyProps::SetStrArray(AnsiString Index, AnsiString S)

    {

      TCouple *Couple;

      Couple = new TCouple();

      Couple->Husband = GetHusband(S);

      Couple->Wife = GetWife(S);

      FObjectProp->AddObject(Index, Couple);

    }

The structure of the PropertyTest program is simple. There is a main form with a button on it. If you click the button, you instantiate an object of type `TMyObject`, as shown in Figure 20.3.
FIGURE 20.3. The main form of the PropertyTest program.

`TMyObject` has five properties, one for each of the major types of properties. These properties have self-explanatory names:

    __property int SimpleProp={read=FSimple, write=FSimple};

    __property TEnumType EnumProp={read=FEnumType, write=FEnumType};

    __property TSetProp SetProp={read=FSetProp, write=FSetProp};

    __property TStringList *ObjectProp={read=FObjectProp, write=FObjectProp};

    __property AnsiString ArrayProp[int i]={read=GetArray};

Before exploring these properties, I should mention that `TMyProps` is descended from the native VCL object called `TCustomControl`. `TCustomControl` is intelligent enough to both display itself on the screen and store itself on the Component Palette. It has several key methods and properties already associated with it, including a `Paint` method and `Width` and `Height` fields.

Because `TCustomControl` is so intelligent, it is easy to use its `Paint` method to write values to the screen:

    void __fastcall TMyProps::Paint()

    {

      Canvas->Brush->Color = clBlue;

      TCustomControl::Paint();

      Canvas->Rectangle(0, 0, Width, Height);

      Canvas->TextOut(1, 1, "FSimple: " + IntToStr(FSimple));

      Canvas->TextOut(1, Canvas->TextHeight("Blaise"), GetArray(0));

      Canvas->TextOut(1, Canvas->TextHeight("Blaise") * 2, FObjectProp->Strings[1]);

    };

Note that you do not need to explicitly call the `Paint` method. Windows calls it for you whenever the object needs to paint or repaint itself. This means that you can hide the window behind others, and it will repaint itself automatically when it is brought to the fore. Inheriting functionality that you need from other objects is a big part of what OOP is all about.

The first three properties of `TMyProps` are extremely easy to understand:

    __property int SimpleProp={read=FSimple, write=FSimple};

    __property TEnumType EnumProp={read=FEnumType, write=FEnumType};

    __property TSetProp SetProp={read=FSetProp, write=FSetProp};

These are direct access properties that simply read to and write from a variable. You can use them with the following syntax:

    M->SimpleProp = 25;

    M->EnumProp = teEnum;

    M->SetProp = TSetProp() << teEnum << teSet;

> NOTE: I once asked one of the developers whether properties such as these didn't waste computer clock cycles. Looking somewhat miffed, he said, "Obviously, we map those calls directly to the variables!"
> Chastened, and somewhat the wiser, I nodded sagely as if this were the answer I expected. Then, I ventured, "So they don't cost us any clock cycles?"
> "Not at runtime, they don't!" he said, and concentrated once again on his debugger, which hovered over some obscure line in `Classes.Pas`.

The syntax for using the `ObjectProp` property is similar to the examples shown previously, but it is a bit harder to fully comprehend the relationship between an object and a property:

    __property TStringList *ObjectProp={read=FObjectProp, write=FObjectProp};

`ObjectProp` is of type `TStringList`, which is a descendant of the `TStrings` type used in the `TListBox.Items` property or the `TMemo.Lines` property. I use `TStringList` instead of `TStrings` because `TStrings` is essentially an abstract type meant for use only in limited circumstances. For general purposes, you should always use a `TStringList` instead of a `TStrings` object. (In fact, neither `TListBox` nor `TMemo` actually uses variables of type `TStrings`. They actually use descendants of `TStrings`, just as I do here.)

> NOTE: A `TStringList` has two possible functions. You can use it to store a simple list of strings, and you can also associate an object with each of those strings. To perform the latter task, call `AddObject`, passing a string in the first parameter and a `TObject` descendant in the second parameter. You can then retrieve the object by passing in the string you used in the call to `AddObject`. `
> TStringLists` do not destroy the objects that you store in them--though they will, of course, clean up the strings you hang on them. It is up to you to deallocate the memory of any object you store on a `TStringList`.
> If you want a simple list object that doesn't have all this specialized functionality, use a linked list or the versatile `TList` object that ships with BCB.

After making the declaration for `ObjectProp` shown earlier, you can now use it as if it were a simple `TStringList` variable. This can sometimes be a bit inconvenient, however. For instance, the following syntax retrieves an object that is associated with a string:

    AnsiString S = "StringConstant";

    Couple = (TCouple*)(FObjectProp->Objects[FObjectProp->IndexOf(S)]);

While not completely beyond the pale, this is definitely not the kind of code you want to have strewn indiscriminately through a program that is going to be maintained by a mere mortal. Furthermore, you must be sure to allocate memory for the `FObjectProp` at the beginning of `TMyProps`'s existence, and you must dispose of that memory in the `TMyProps` destructor:

    __fastcall TMyProps::TMyProps(TComponent *AOwner)

     :TCustomControl(AOwner)

    {

      ... // Code omitted here

      FObjectProp = new TStringList();

    };

    __fastcall TMyProps::~TMyProps()

    {

      int i;

      for (i = 0; i < FObjectProp->Count; i++)

      {

        FObjectProp->Objects[i]->Free();

      }

      FObjectProp->Free();

    }

The key point is that you must iterate through the string list at the end of the program, deallocating memory for each object you store on the list. Then you must deallocate the memory for the `TStringList` itself.

There is nothing you can do about the necessity of allocating and deallocating memory for an object of type `TStringList`. You can, however, use array properties to simplify the act of accessing it, and to simplify the act of allocating memory for each object you store in it. PropertyTest shows how this can be done.

The code entertains the conceit that you are creating a list for a party to which only married couples are invited. Each couple's last name is stored as a string in a `TStringList`, and their first names are stored in an object that is stored in the `TStringList` in association with the last name. In other words, PropertyTest calls `AddObject` with the last name in the first parameter and an object containing their first names in the second parameter. This sounds complicated at first, but array properties can make the task trivial from the user's point of view.

In the PropertyTest program, I store a simple object with two fields inside the `TStringList`:

    class TCouple: public TObject

    {

    public:

      AnsiString Husband;

      AnsiString Wife;

      TCouple() {}

    };

Note that this object looks a lot like a simple struct. In fact, I would have used a record here, except that `TStringList`s expect `TObject` descendants, not simple records. (Actually, you can sometimes get away with storing non-objects in `TStringList`s, but I'm not going to cover that topic in this book.)

As described earlier, it would be inconvenient to ask consumers of `TMyObject` to allocate memory for a `TCouple` object each time it needed to be used. Instead, PropertyTest asks the user to pass in first and last names in this simple string format:

    "HusbandName, WifeName"

PropertyTest also asks them to pass in the last name as a separate variable. To simplify this process, I use a string array property:

    __property AnsiString StrArrayProp[AnsiString i]=

        {read=GetStrArray,write=SetStrArray};

Notice that this array uses a string as an index, rather than a number!

Given the `StrArrayProp` declaration, the user can write the following code:

    M->StrArrayProp["Jones"] = "Sam, Mary";

This is a simple, intuitive line of code, even if it is a bit unconventional. The question, of course, is how can BCB parse this information?

If you look at the declaration for `StrArrayProp`, you can see that it has two access methods called `GetStrArray` and `SetStrArray`. `SetStrArray` and its associated functions look like this:

    AnsiString GetHusband(AnsiString S)

    {

      return StripLastToken(S, `,');

    }

    AnsiString GetWife(AnsiString S)

    {

      return StripFirstToken(S, `,');

    }

    void TMyProps::SetStrArray(AnsiString Index, AnsiString S)

    {

      TCouple *Couple;

      Couple = new TCouple();

      Couple->Husband = GetHusband(S);

      Couple->Wife = GetWife(S);

      FObjectProp->AddObject(Index, Couple);

    }

Note the declaration for `SetStrArray`. It takes two parameters. The first one is an index of type `string`, and the second is the value to be stored in the array. So, `"Jones"` is passed in as an index, and `"Sam, Mary"` is the value to be added to the array.

`SetStrArray` begins by allocating memory for an object of type `TCouple`. It then parses the husband and wife's names from the string by calling two token-based functions from the `CodeBox` unit that ships with this book. Finally, a call to `AddObject` is executed. When the program is finished, you must be sure to deallocate the memory for the `TCouple` objects in the `destructor.`

The twin of `SetStrArray` is `GetStrArray`. This function retrieves a couple's last name from the `TStringList` whenever the user passes in a last name. The syntax for retrieving information from the `StrArray` property looks like this:

    AnsiString S = M->StrArrayProp["Doe"];

In this case, `S` is assigned the value `"Sam, Mary"`. Once again, note the remarkable fact that BCB enables us to use a string as an index in a property array.

The implementation for `GetStrArray` is fairly simple:

    AnsiString __fastcall TMyProps::GetStrArray(AnsiString S)

    {

      TCouple *Couple;

      Couple = (TCouple*)(FObjectProp->Objects[FObjectProp->IndexOf(S)]);

      return Couple->Husband + ", " + Couple->Wife;

    }

The code retrieves the object from the `TStringList` and then performs some simple hand-waving to re-create the original string passed in by the user. Obviously, it would be easy to add additional methods that retrieved only a wife's name, or only a husband's name.

I'm showing you this syntax not because I'm convinced that you need to use `TStringList`s and property arrays in exactly the manner showed here, but because I want to demonstrate how properties can be used to conceal an implementation and hide data from the user. The last two properties declared in this program show how to use important property types, and they also demonstrate how properties can be used to reduce relatively complex operations to a simple syntax.

Consumers of this object don't need to know that I am storing the information in a `TStringList`, and they won't need to know if I change the method of storing this information at some later date. As long as the interface for `TMyObject` remains the same--that is, as long as I don't change the declaration for `StrArrayProp`--I am free to change the implementation at any time.

There is one other array property used in this program that should be mentioned briefly:

    __property AnsiString ArrayProp[int i]={read=GetArray};

`ArrayProp` uses the traditional integer as an index. However, note that this array still has a special trait not associated with normal arrays: It is read-only! Because no write method is declared for this property, it cannot be written to; it can be used only to query the `TStringList` that it ends up addressing:

    AnsiString __fastcall TMyProps::GetArray(int Index)

    {

      return FObjectProp->Strings[Index];

    }

You can call `ArrayProp` with this syntax:

    AnsiString S = M->ArrayProp[0];

This is an obvious improvement over writing the following:

    AnsiString S = M->FObjectProp->Strings[0];

Creating a simple interface for an object might not seem important at first, but in day-to-day programming a simple, clean syntax is invaluable. For instance, the PropertyTest program calls `ArrayProp` in the following manner:

    for (i = 0; i < M->ObjectProp->Count; i++)

        ListBox2->Items->Add(M->ArrayProp[i]);

> NOTE: Astute readers might be noticing that BCB is flexible enough to enable you to improve even its own syntax. For instance, if you wanted to, you could create a list box descendant that enables you to write this syntax:

>     ListBox2->AddStr(S);

> instead of

>     ListBox2->Items->Add(S);

> In Chapter 22, you will see that you can even replace the `TListBox` object on the com-ponent palette with one of your own making! The techniques you are learning in these chapters on the VCL will prove to be the key to enhancing BCB so that it becomes a custom-made tool that fits your specific needs.

If you bury yourself in the BCB source code, eventually you might notice the `default` directive, which can be used with properties:

    __property char Default1={read=FDefault1, write=FDefault1, default= `1'};

Looking at this syntax, one would tend to think that this code automatically sets `FDefault1` to the value ``1'`. However, this is not its purpose. Rather, it tells BCB whether this value needs to be streamed when a form file is being written to disk. If you make `TMyProp` into a component, drop it onto a form, and save that form to disk, BCB explicitly saves that value if it is not equal to `1`, but skips it if it is equal to `1`.

An obvious benefit of the `default` directive is that it saves room in DFM files. Many objects have as many as 25, or even 50, properties associated with them. Writing them all to disk would be an expensive task. As it happens, most properties used in a form have default values that are never changed. The `default` directive merely specifies that default value, and BCB thus knows whether to write the value to disk. If the property in the Object Inspector is equal to the default, BCB just passes over the property when it's time to write to disk. When reading the values back in, if the property is not explicitly mentioned in the DFM file, the property retains the value you assigned to it in the component's constructor.

> NOTE: The property is never assigned the default value by BCB. You must ensure that you assign the default values to the properties as you indicated in the class declaration. This must be done in the constructor. A mismatch between the declared default and the actual initial value established by the constructor will result in lost data when streaming the component in and out:

>     __fastcall TMyProps::TMyProps(TComponent *AOwner)

>      :TCustomControl(AOwner)

>     {

>       ... // Code omitted here

>       Default1 = `1';

>     };

> Similarly, if you change the initial value of an inherited published property in your constructor, you should also reassert/redeclare (partial declaration) that property in your descendant class declaration to change the declared default value to match the actual initial value.
> The `default` directive does nothing more than give BCB a way of determining whether it needs to write a value to disk. It never assigns a value to any property. You have to do that yourself in your constructor.

Of course, there are times when you want to assign a property a default value at the moment that the object it belongs to is created. These are the times when you wish the `default` directive did what its name implies. However, it does not, and never will, perform this action. To gain this functionality you must use the constructor, as shown in the PropertyTest application.

There are a few occasions when the constructor won't work for you because of the current state of a property. In those cases, you can use the `Loaded` or `SetParent` methods to initialize the value of a property. If you use the `Loaded` method to initialize a property, the results of the initialization won't show up at design-time but will become evident at runtime.

After reading this section, it should be clear that array properties represent one of the more powerful and flexible aspects of BCB programming. Though they are quite similar to operator overloading, they have their own special qualities and advantages.

### Summary

That wraps up this introduction to properties and encapsulation. The key items you have explored are the `private`, `protected`, `public`, and __`published` directives, as well as the art of creating useful properties. I have also attempted to browbeat you with the importance of hiding data and methods. Remember that robust, easily maintainable objects never directly expose their data! Instead, they present the user with a simple, custom-designed interface that should usually be easy to use.

In the next chapter, you will learn about polymorphism, which is the crown jewel of object-oriented theory.

©Copyright, Macmillan Computer Publishing. All rights reserved.

| Use of this site is subject certain Terms & Conditions. Copyright (c) 1996-1999 EarthWeb, Inc.. All rights reserved. Reproduction in whole or in part in any form or medium without express written permission of EarthWeb is prohibited. Please read our privacy policy for details.