|  | 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 ## - 19 - ## Inheritance This chapter focuses on object-oriented programming (OOP) as it applies to the VCL. Specifically, it takes a close look at inheritance, one of the big-three topics in object-oriented code. The other two key topics are encapsulation and polymorphism, which you learn about in the next two chapters. Even if you already know all about OOP, you should still at least skim this chapter so you can learn about the difference between VCL objects and standard C++ objects. In particular, this chapter covers the following topics: OOP theory and basics VCL object construction Inheritance Virtual methods Aggregation Form inheritance The text focuses on several programs designed to show how objects are constructed. One of the programs is developed in several stages so that you can see how an object hierarchy emerges out of a set of raw ideas. After you read the next three chapters on inheritance, encapsulation, and polymorphism, the next big step is to learn how to build components. In fact, the real justification for learning this material is that it gives you the ability to start creating your own components. Building your own components is one of the most important tasks you can tackle in BCB, so I will lay the groundwork for it carefully. It is important to understand that the next three chapters are aimed at programmers who want to work inside the VCL. I make no attempt to do justice to all the complex features of the C++ object model. Instead, I try to present you with a subset of those features as they apply to the VCL. This means that I make short shrift of interesting topics such as function and operator overloading. My intent, however, is to show you how to create components. You do not have to be an expert in C++ OOP theory to achieve that goal. For all its wonders, I don't think there is anything in C++Builder that even approaches the significance of components. VCL components are the most amazing technological achievement I have seen in contemporary programming. If you want to do something really fantastic with your computer, then pay attention to the next few chapters so that you can learn how to build great components. When reading this chapter, you might want to make use of the ClassBrowser sample program that ships with BCB. It allows you to explore the hierarchy of the VCL. This program is found in the `Examples/ClassBrw` directory. It is far from perfect, but it will serve to give you an overview of the VCL classes. You should also go to `www.object-domain.com` and see whether they have a version of Snorkle for C++Builder available. The versions of Snorkle for Delphi that I have seen are very nice indeed, and if they can duplicate their efforts in the world of C++, then most readers of this book will want to test their technology. ### About Objects It might seem a little strange to start focusing on objects this late in the book. After all, almost every program I have shown so far uses object-oriented code. So how could I wait this long to begin talking seriously about objects? To answer this question, I need to discuss two different issues: How does BCB treat objects? Why do people write object-oriented code? The developers wanted BCB to be very easy to use. By its very nature, OOP is not always a simple topic. As a result, BCB goes to considerable lengths to hide some of the difficulties of object-oriented programming from the user. The biggest steps in this direction include the automatic construction of `Form1` as an object and the existence of the delegation model. The fact that the scaffolding for most methods is produced automatically by the IDE is one of the key ways the product saves time--and one of the key ways it eases the process of producing applications. The simple fact is that some people would never be able to approach BCB if they had to go through the process of writing all this every time they created a form:
    //--------------------------------------------------------------------------

    #ifndef Unit1H

    #define Unit1H

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

    #include <vcl\Classes.hpp>

    #include <vcl\Controls.hpp>

    #include <vcl\StdCtrls.hpp>

    #include <vcl\Forms.hpp>

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

    class TForm1 : public TForm

    {

    __published:

    private:

    public:        // User declarations

      virtual __fastcall TForm1(TComponent* Owner);

    };

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

    extern TForm1 *Form1;

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

    #endif
I'm leaving out the implementation of the constructor, and a few other features, but in a stripped-down form, this code is indeed the basis for most BCB units. It's simple enough to write; nonetheless, it could form a barrier between the product and certain types of programmers. The next obvious question is, "Why did the developers choose to write object-oriented code if the subject itself can at times become somewhat complex? Why not just use the relatively simpler framework provided by structured programming?" The answer is that although it is simpler to create small structured programs than small object-oriented programs, it's easier to write large object-oriented programs than it is to write large structured programs. OOP brings discipline and structure to a project. In the long run, this makes coding easier. The problem is the learning curve associated with understanding OOP. Almost everyone agrees that it's easier to finish a group project if you appoint a leader for the group; it's easier to win at sports if you practice regularly; and, ultimately, it's easier to become a good musician if you sit through some boring lessons with a professional. It also might seem at first as if structured programs are simpler to learn how to write and, therefore, are simpler to write, but this isn't true. Just as it helps to take lessons, practice, and learn discipline if you want to become good at playing a sport or a musical instrument, it helps to learn object- oriented code if you want to write good programs. Here's another way of stating the same matter. There is nothing you can do with object- oriented code that you can't also do with structured programming. It's just that OOP makes it relatively easy to construct programs that are fundamentally sound and easily maintained. This doesn't mean you can't write structured programs that are every bit as architecturally sound as object-oriented programs. The problem, however, is that it is very difficult to design a structured program that is truly modularized and truly easy to maintain. Object-oriented code, on the other hand, has a natural tendency to move you in the direction of a sound, well-structured design. The thesis of this chapter is that object-oriented code is basically a technique for designing robust, well-planned programs. The syntax of OOP emerged out of the desire to help programmers design applications that work. It is perhaps arguable as to whether or not OOP by itself succeeded in achieving its goal, though certainly I personally believe that it is a success. However, I think it is undeniable that OOP in conjunction with components is the answer to many core programming problems. If your only experience with components is in creating ActiveX controls, then you haven't yet seen what this technology can do. The combination of OOP and components is something that can make programmers many times more productive than they had ever imagined possible when writing structured code, or when working with either objects or components alone. NOTE: It's probably worth pointing out that OOP is not a separate subject from structured programming but its natural child. OOP emerged out of the same types of thinking that generated structured code. Much of what is true in structured programs is also true in object-oriented programs, except OOP takes these theories much further. Object-based programmers should know nearly everything that structured programmers know and should then add another layer of information on top of it. OOP is certainly not the end-all and be-all of programming. Rather, it is an intermediate step in an ongoing process that might never have an end. BCB, with its heavy use of components, already shows part of what the future holds. In particular, the future is about components and visual manipulation of objects. The Object Inspector enables you to see inside objects and to start to manipulate them visually. You can do this without having to write code. It is quite likely that this trend will continue in the future, and you will start to see programs not as code but as a series of objects depicted as a hierarchy. If programmers want to manipulate these objects, they will be able to do so through tools such as the Object Inspector or through other means currently being used only in experimental languages. To take this out of the clouds for a moment, here is my list of what's best about BCB: Visual design tools A component architecture replete with a delegation model A real object-oriented language Here are the same ideas looked at again from a slightly more in-depth perspective: Visual Tools: You can easily design a form using visual tools. To create a useful form, you want to be able to arrange and rearrange the elements of the visual design quickly and easily. BCB excels at this. Components: You want to be able to manipulate objects not only as code, but as seemingly physical entities you can handle with the mouse. Components provide an ideal solution to this problem. For example, the plastic Lego sets you played with as a child were fascinating because they enabled you to build complex structures out of simple, easy-to-manipulate pieces. In other words, Legos let you concentrate on the design of structures, making the actual construction of a robust and easy-to-maintain building relatively trivial. Components give the same kind of flexibility. OOP: Objects, and particularly the ability to view object hierarchies in a browser, make it easy to see the overall design of a program. It's possible to see how a program is constructed not only by looking at the code, but also by looking at an object hierarchy made up of reusable classes. Use the ClassBrowser example or a copy of Snorkle to view these hierarchies as they appear in your own programs. These kinds of abstract, visual representations of a code base aid in the process of designing and maintaining a program. A key word here is reuse. You can write an object once and then use it over and over again. Reusability is what OOP is all about. OOP, then, is part of a theory of design that is moving increasingly in the direction of reusable, visual components that can be manipulated with the mouse. Undoubtedly, this means that some types of programs that are difficult to construct today will become trivial to build in the future. BCB has already performed this magic with databases. A 10-year-old child could use BCB to construct a simple database application. However, creating complex programs will probably always be difficult, simply because it is so hard to design a good program that performs anything more than trivial tasks. First printing presses, then typewriters, and finally word processors have made writing much easier than it used to be, but they have not succeeded in making us all into a race of Shakespeares. NOTE: One thing that is not built into BCB that can help you create robust programs is a good object-modeling tool. There are some tools, such as the products called WithClass and Snorkle, that are designed to work with the Delphi VCL and should soon appear in a BCB-based format. It is worth pointing out that it is easy to draw object hierarchies using some form of custom or agreed upon notation. Programs such as Visio or Playground can help with this process. Even if there is no direct code generation involved, there is still an enormous benefit to be derived from this process. I have worked on projects I thought had gone hopelessly astray and could not ever be salvaged. These "lost causes" were saved by simply drawing out my object hierarchy with a tool that would let me rearrange its elements in several different patterns. C++ is a great language, but it offers no means for providing an overview of your object hierarchy. Drawing the object hierarchy with a simple object notation can help enormously when it is not clear how to design a particular feature or when you need to try to salvage a product that has gone astray. BCB's object-oriented, component-based architecture makes programming easier than it used to be. That doesn't mean that now everyone will be able to program. It just means that now the best programmers can make better applications. The key terms are reuse, visual design tools, components, and objects. If you can find an object-modeling tool that can aid in program development, you will be even further ahead. ### Creating Simple Objects To start a discussion of objects, it might be a good idea to cut the VCL out of the picture as much as possible. This will eliminate the complex object hierarchy associated with the VCL. In its place, you can construct some very simple objects with a known hierarchy that is easy to define. As the discussion progresses, the VCL can be introduced into the programs in a planned and sensible manner. 1. Start a new project. 2. Bring up the Project Manager from the View menu and remove `Form1.cpp` and the project resource file. 3. Go to the View menu again and choose Project Source. 4. Go to Options | Project | Linker and choose Console application, as shown in Figure 19.1. FIGURE 19.1. Creating a console application in BCB. Edit the main source file for the project so it looks like this:
    #include <stdio.h>

    int main(void)

    {

      printf("Daughters of Time, the hypocritic Days,\n");

      printf("Muffled and dumb like barefoot dervishes\n");

      printf("-- Ralph Waldo Emerson");

      return 0;

    }
Save this file as `Object1.mak`. It is now a complete application that circumvents the VCL. If you open up a DOS window and run the program from the DOS prompt, the output looks like Figure 19.2. FIGURE 19.2. The output from the first take of the OBJECT1 program as it appears when run from the DOS prompt. It might seem strange to you that I have gone out of my way to eliminate so much of the object hierarchy in a chapter that is about objects. My goal, however, is to clear the boards so that you can view objects in a simplified state, thereby clearly delineating their most salient points. The program that unfolds through the next few pages is called OBJECT1. This is a very simple object-oriented program that you will build on the console application framework established earlier. I'm not going to start by showing you the code for the whole program, because I want you to build it one step at a time so that its structure emerges little by little. To begin, you should create a small object at the top of the program:
    class TMyObject

    {

    };

    int main(void)

    {

      return 0;

    }
All I have done here is added a simple class definition and removed the `printf()`statements. Delphi programmers should note that this class is not a descendant of `TObject`, even though it would have been in Object Pascal. One of the fundamental rules of Object Pascal programming is that it is impossible to build an object that is not a descendant of `TObject` or one of `TObject`'s children. The reason for this rule is that `TObject` contains some RTTI-based intelligence that is needed by all BCB objects. This same intelligence is present in the metaclass that is part of the BCB version of `TObject`. However, you can create C++ objects that do not descend from `TObject` and thus do not include this intelligence. NOTE: You will find that I use the words class and object almost completely interchangeably. This is technically correct, although there is some merit in using the word class to describe the written declarations that appear in a text file and object to refer to a compiled class that is part of a binary file. In other words, programs are made up of objects, whereas source files show class definitions. However, this distinction is not one that I spend a great deal of time stressing in this book. To create a true VCL object, you should change `TMyObject`'s definition so that it reads as follows:
    #include <vcl\vcl.h>

    class TMyObject : public TObject

    {

    public:

      __fastcall TMyObject(void) : TObject() {};

    }

    int main(void)

    {

      return 0;

    }
Logically, there is now a considerable difference between this declaration and the one you created earlier. In particular, this is now a VCL object and must be created on the heap. It also supports VCL specific syntax such as the `__published` directive. All VCL objects must have a constructor, and it should be declared `__fastcall`. Methods or functions declared `__fastcall` can have some of their parameters passed in registers, rather than always being pushed on the stack. This is the calling convention used by VCL constructors, so you should conform to it. All VCL objects that are descendants of `TComponent` must have destructors declared `__fastcall virtual`:
    __fastcall virtual TComponent(TComponent* Aowner);
The reason for the `__fastcall` and `virtual` restrictions has to do with conformance to the VCL programming model. In particular, the VCL declares the constructor for `TComponent` as `virtual`, so C++ objects that descend from `TComponent` must follow along. This means that all components you create must be declared with `virtual` constructors, because all components are, at least in practice, descendants of `TComponent`. I comment on this fact simply because it is very unusual for C++ constructors to be declared `virtual`. NOTE: Actually, it is theoretically possible for you to create your own class that performs the same chores that `TComponent` performs. There is nothing magical about `TComponent`; it simply contains standard VCL code that makes it possible for an object to live on the Component Palette. It is not practical to duplicate this effort in your own code, and so one could perhaps go so far as to say that "by definition" all components are descendants of `TComponent`. However, this is not strictly true, as you could create your own class that appears on the Component Palette without descending from `TComponent`. I personally cannot imagine any set of circumstances that would justify the effort involved in duplicating the work done in `TComponent`. The declaration and implementation for a C++ constructor can have two forms. They can appear entirely inside a class declaration, or you can split them up, with the declaration inside the class and the implementation outside:
    #include <vcl\vcl.h>

    class TMyObject : public TObject

    {

    public:

      __fastcall TMyObject(void);

    };

    __fastcall TMyObject::TMyObject(void) : TObject()

    {

    }

    int main(void)

    {

      return 0;

    }
When you implement a constructor, you should follow the header with a colon and a call to the ancestor's constructor:
    _fastcall TMyObject::TMyObject(void) : TObject()
Now that you have an overview of a basic VCL object declaration, the next step is to declare a variable of type `TMyObject` and then instantiate it and dispose of it:
    class TMyObject : public TObject

    {

    public:

      __fastcall TMyObject() : TObject()  {}

    };

    int main(void)

    {

      TMyObject *MyObject = new TMyObject;

      delete MyObject;

      return 0;

    }
The code shown here doesn't do anything functional. Its only purpose is to teach you how objects work. Specifically, it declares a variable of type `TMyObject`:
    TMyObject *MyObject
Next, it allocates the memory for the object:
    new TMyObject;
Put together on one line, the statement looks like this:
    TMyObject *MyObject = new TMyObject;
This statement actually creates a pointer variable of type `TMyObject`. In VCL programming, you have to take this step if you want to use `MyObject`, and, furthermore, you must dispose of this memory when you are finished with it. There are two ways to destroy an object. One is to call `Free`, and the other is to use the `delete` operator:
    MyObject->Free();

    delete MyObject;
Both techniques have the same outcome. I believe the majority of people prefer `delete`, and it is what I use most often in the code found in this book. However, there are reasons you might want to call `Free`, so I will discuss it in the next few paragraphs. When you free an object, what you are really doing is calling the object's destructor. The following code shows approximately what takes place in the `Free` method of `TObject`:
    procedure TObject.Free;

    begin

      if Self <> nil then

        Destroy;

    end;
The variable `Self` always points to the current object. It plays the same role in Object Pascal that `this` plays in C++. If you are inside one of the methods of an object, you can refer to that object by using `Self`. (`Self` is passed as an implicit parameter to all BCB methods.) Here is how the VCL `Free` method would look in C++:
    void __fastcall TObject::Free()

    {

      if (this != NULL)

       ~TObject();

    }
NOTE: Programmers use the words descendant, child object, derived class, and subclass as synonyms. I prefer to use either descendant or child object, because subclass is also used in another context and derived class seems unnecessarily obscure. My feeling is that it's best to stick to one metaphor: parent, child, ancestor, and descendant, where child and descendant are synonymous, and parent and ancestor are synonymous. Standard C++ does not define a `Free` method. This is something specific to the VCL. It is added to the VCL to make objects easier to use. It is very bad to call the destructor of an object that no longer exists. As a result, the `Free` method is there to provide a check that gives you some measure of protection against this error. Despite this, the general consensus is that it is best to call delete. The great virtue of delete is that it looks like standard C++ code, and C++ programmers care a lot about standards. Now that you know how to declare, allocate, and deallocate a simple object, it's time to narrow the focus and tackle the subject of inheritance. The next two sections are dedicated to this chore--specifically, to explaining the relationship between a parent and child object. ### Understanding Inheritance In general, a child object can use any of its parent's methods. A descendant of an object gets the benefit of its parent's capabilities, plus any new capabilities it might bring to the table. I say that this is true in general, because the `private` directive can limit the capability of a child to call some of its parent's routines. The `private` directive is explained in depth later in this chapter. Except for its constructor, all of `TMyObject`'s methods and fields are inherited:
    class TMyObject : public TObject

    {

    public:

      __fastcall TMyObject() : TObject()  {}

    };
This declaration is somewhat deceiving because `TObject` contains many methods that are available to instances of `TMyObject`. In other words, `TMyObject` is not quite as simple an object as it appears at first. So, what are all these methods associated with `TObject`? Well, you can see their definitions, as well as their implementations, if you open up the `SysDefs.h` file from the `\BCB\Include\VCL` subdirectory:
    class   __declspec(delphiclass) TObject

    {

      public:

        __fastcall TObject() {}

        __fastcall Free();

        TClass __fastcall ClassType();

        void __fastcall CleanupInstance();

        void * __fastcall FieldAddress(const ShortString &Name);

        static TObject * __fastcall InitInstance(TClass cls, void *instance);

        static ShortString __fastcall ClassName(TClass cls);

        static bool __fastcall ClassNameIs(TClass cls, const AnsiString string);

        static TClass __fastcall ClassParent(TClass cls);

        static void * __fastcall ClassInfo(TClass cls);

        static long __fastcall InstanceSize(TClass cls);

        static bool __fastcall InheritsFrom(TClass cls, TClass aClass);

        static void * __fastcall MethodAddress(TClass cls, const ShortString &Name);

        static ShortString __fastcall MethodName(TClass cls, void *Address);

        ...// Code omitted here

        virtual void __fastcall Dispatch(void *Message);

        virtual void __fastcall DefaultHandler(void* Message);

      private:

        virtual TObject* __fastcall NewInstance(TClass cls);

      public:

        virtual void __fastcall FreeInstance();

        virtual __fastcall ~TObject() {}

    };
You can find the entire implementation of `TObject` in the Object Pascal `System.pas` unit that ships with BCB. Much of it is actually in assembler, but the source is there if you want to study it. You should, of course, also examine the declaration for `TObject` in `SysDefs.h`. The calls in `SysDefs.h`, however, ultimately resolve into calls to the Pascal implementation in `System.pas`. I should perhaps add that the `TObject` declaration in `SysDefs.h` is very hard to understand, but I promise you that it does end up resolving into calls that access the `System.pas` version of `TObject`. NOTE: Although I have mentioned this subject before, it's probably once again time to stress the importance of viewing the Pascal source code to the VCL. Your version of BCB might or might not ship with the source, but if you don't have it and can possibly afford to buy it, you should think seriously about obtaining it. You should peruse the `BCB\Include\VCL` subdirectory that contains the header files for the imported VCL Pascal units. These files provide the interface for key BCB units. They are not as good as having the source, but they are very valuable. I refer to both the header files and the Pascal source continuously. You can see that `TObject` has a few basic functions declared right at the top:
    __fastcall TObject() {}

    __fastcall Free();

    virtual __fastcall ~TObject() {}
The point to grasp here is that `TMyObject` has a destructor and `Free` method because it inherits them from `TObject`. To understand this point, you can add a line of code to the nascent OBJECT1 program:
    #include <conio.h>

    #include <stdio.h>

    int main(void)

    {

      TMyObject *MyObject = new TMyObject;

      AnsiString S = MyObject->ClassName();

      printf(S.c_str());

      delete MyObject;

      getch();

      return 0;

    }
This code enables the object to write its name to the screen. The output from this program is a single string:
    TMyObject
When you run the program, this string might flash by too quickly for leisurely perusal. To remedy the situation, add a `getch()` at the very end of the code, right before the`return` statement. To end this program, press Enter. (That's the way it used to be done back in the DOS world.) If you want to, you can even get this object to say its parent's name:
    int main(void)

    {

      TMyObject *MyObject = new TMyObject;

      printf(Format("ClassName: %s\nParent's ClassName: %s",

        OPENARRAY(TVarRec, (

          MyObject->ClassName(),

          MyObject->ClassParent()->ClassName()))).c_str());

      delete MyObject;

      getch();

      return 0;

    }
The output from this code is the following:
    ClassName: TMyObject

    Parent's ClassName: TObject
The point, of course, is that `TMyObject` inherits quite a bit of functionality from its parent, and, as a result, it has numerous capabilities that might not be obvious from merely viewing its declaration. The ability to trace an object's ancestry is relatively appealing, so it might be nice to add it to `TMyObject` as a method:
    #include <vcl/vcl.h>

    #include <stdio.h>

    #include <conio.h>

    #include "classrefs.h"

    USEUNIT("ClassRefs.cpp");

    class TMyObject : public TObject

    {

    public:

      TMyObject() : TObject()  {}

      void PrintString(AnsiString S);

      void ShowHierarchy();

    };

    void TMyObject::PrintString(AnsiString S)

    {

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

    }

    void TMyObject::ShowHierarchy()

    {

      TClass AClass;

      AnsiString AClassName = AnsiString(ClassName()).c_str();

      PrintString(AClassName);

      AClass = ClassParent();

      while (AClass)

      {

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

        PrintString(AClassName);

        AClass = AClass->ClassParent();

      }

    }

    int main(void)

    {

      ShowClassReferences();

      TMyObject *MyObject = new TMyObject;

      MyObject->ShowHierarchy();

    delete MyObject;

      getch();

      return 0;

    }
This version of the OBJECT1 program includes two methods, listed in the `TMyObject` class declaration:
    class TMyObject : public TObject

    {

    public:

      TMyObject() : TObject()  {}

      void PrintString(AnsiString S);

      void ShowHierarchy();

    };
Take a look at the implementation for `ShowHierarchy`. Perhaps the first thing you notice in it is the class reference in the first line, which uses the `TClass` type. The type `TClass` is an object reference and is declared in `Sysdefs.h` as follows:
    typedef TMetaClass* TClass;
Because `ClassParent` returns a variable of type `TClass`, it is obviously what needs to be used here. NOTE: An object reference is a special metaclass that can be assigned to an object. Here is a unit that shows some legal uses of an object reference:

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

    // ClassRefs.cpp

    // Object1

    // copyright (c) 1997 by Charlie Calvert

    //

    #include <vcl\vcl.h>

    #include <forms.hpp>

    #pragma hdrstop

    #include "ClassRefs.h"

    TClass AClass;

      class TDescendant: public TObject

      {

      public:

        TDescendant(): TObject() {}

>       };

>     void ShowClassReferences()

>     {

>       printf("** Start object references **\n");

>       AClass = __classid(TObject);

>       printf("%s\n", AnsiString(AClass->ClassName()).c_str());

>       AClass = __classid(TDescendant);

>       printf("%s\n", AnsiString(AClass->ClassName()).c_str());

>       AClass = __classid(TForm);

>       printf("%s\n", AnsiString(AClass->ClassName()).c_str());

>       printf("** End object references **\n\n");

>     }

> Notice that you do not have to create an object before you can use it with an `object reference`. In general, you can call any of the static methods of `TObject` with a class reference.
> You cannot use an object reference to refer to a field that belongs only to the child of the `object reference` type. For instance, this code does not compile because `Caption` is not a property of `TObject`:

>     ObjectRef =  __classid(TForm)

>     ObjectRef.Caption := `Sam';

>     WriteLn(ObjectRef.Caption);

> You will find a version of the `CLSREF` unit in the same subdirectory as OBJECT1. You can use the Project Manager to add this file to the project, and you can then call it in the second line of the body of the OBJECT1 program. However, you should not leave this unit as part of the project, because it will muddy the view of the object hierarchy that you get in the Browser.
> There are not that many times in which you need to use an object reference in day-to-day programming. If you are not totally clear on what they do, you can probably afford to skip the subject. If you really want to know more, you should examine `Sysdefs.h`; recognize that the `TMetaClass` you see there is the C++ way of creating a feature that exists in the VCL. The reason the VCL supports this feature is that it needs fairly extensive Run Time Type Information (RTTI) in order to run, and it gets a good portion of that information from the methods in `TObject` that are part of `TMetaClass` and are in turn used in an object reference.

When you get past the object reference, the remaining portions of the `ShowHierarchy` method are fairly straightforward:

    void TMyObject::ShowHierarchy()

    {

      TClass AClass;

      AnsiString AClassName = AnsiString(ClassName()).c_str();

      PrintString(AClassName);

      AClass = ClassParent();

      while (AClass)

      {

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

        PrintString(AClassName);

        AClass = AClass->ClassParent();

      }

    }

This code first writes the `ClassName` of the current object, which is `TMyObject`. Then it gets the `ClassParent`, which is `TObject`, and writes its name to the screen. The code then tries to get `TObject`'s parent and fails, because `TObject` has no parent. At this point, `AClass` is set to `NULL` and the code exits the `while` loop. The output for the program is shown in Figure 19.3.
FIGURE 19.3. The output from the OBJECT1 program.

In this section, you have learned about the `Create`, `Destroy`, `Free`, `ClassParent`, and `ClassName` methods of `TObject`. The declaration of `TObject` shows that several other methods are available to BCB programmers. However, I do not discuss these methods in depth because they are either self-explanatory (`InheritsFrom`) or beyond the scope of this book. I should mention, however, that some of these routines are used by the compiler itself when dispatching routines or performing other complex tasks that usually require RTTI support. These are advanced programming issues that impact only a very small percentage of BCB programmers.

### Virtual Methods

Inheritance, in itself, is an interesting feature, but it would not take on much significance were it not for the presence of virtual methods. Virtual methods can be overridden in a descendant class. As such, they provide the key to polymorphism, which is a trait of OOP programs that enables you to give the same command to two different objects but have them respond in different ways. This chapter introduces polymorphism, but I will leave the more complex aspects of this subject for Chapter 21, titled, appropriately enough, "Polymorphism." Polymorphism is a relatively difficult subject to grasp; therefore, I have stretched out a full explanation of it over several chapters.

Unlike Object Pascal, BCB has only one type of virtual method. This directive tells the compiler to store the address of the function in a virtual method table.

> NOTE: Delphi programmers should note that C++ does not support either the dynamic or message directives. In their place, you can use the `MESSAGE_MAP` macro.

The OBJECT2 program (shown in Listing 19.2) has one `virtual` method. The `virtual` method is overridden in a child object. When you are creating the OBJECT2 program, you should start with the source code for the OBJECT1 program. Modify the code by declaring `PrintString` as `virtual` and by creating a descendant of `TMyObject` called `THierarchy`. Also, don't forget to make sure the program is set to work as a console application. If you don't have this option checked, you can get an `EInOutError` exception. After changing the setting, you should also rebuild your project so the new option takes effect.

> NOTE: When creating one project based on another, you can often copy the code from the directory where the first project is stored into a separate directory made for the second project. After copying the project, it is probably simplest to delete everything but the actual source files from the new directory. For instance, delete the DSK file, the MAK file, and any other extraneous files you will not need. Then create a new project, delete its main form, and add copies of the source files you want to reuse from the previous project. Otherwise, you might find paths hard-coded into your DSK or MAK files that address files stored in the first program's directory. In this particular case, it is probably easiest just to re-create the project entirely from scratch, but the information in this note can be used as a general set of guidelines for use when copying projects from one directory to another.

Listing 19.2. The source code for the main unit in the OBJECT2 program.

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

    // Object2.cpp

    // Project: Object2

    // Copyright (c) 1997 by Charlie Calvert

    //

    #include <vcl/vcl.h>

    #include <stdio.h>

    #include <conio.h>

    class TMyObject :public TObject

    {

    public:

      TMyObject() : TObject()  {}

      void ShowHierarchy();

      virtual void PrintString(AnsiString S);

    };

    class THierarchy: public TMyObject

    {

      int FColor;

    public:

      THierarchy() : TMyObject() {}

      virtual void PrintString(AnsiString S);

      __property int Color={read=FColor,write=FColor};

    };

    void TMyObject::PrintString(AnsiString S)

    {

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

    }

    void TMyObject::ShowHierarchy()

    {

      TClass AClass;

      AnsiString AClassName = AnsiString(ClassName()).c_str();

      PrintString(AClassName);

      AClass = ClassParent();

      while (AClass)

      {

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

        PrintString(AClassName);

        AClass = AClass->ClassParent();

      }

    }

    void THierarchy::PrintString(AnsiString S)

    {

      char Temp[250];

      textcolor(FColor);

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

      cputs(Temp);

    }

    int main(void)

    {

      TMyObject *MyObject = new TMyObject();

      MyObject->ShowHierarchy();

      MyObject->Free();

      THierarchy *Hierarchy = new THierarchy();

      Hierarchy->Color = YELLOW;

      Hierarchy->ShowHierarchy();

      Hierarchy->Free();

      getch();

      return 0;

    }

In OBJECT1, the `ShowHierarchy` method wrote its output to the screen. Suppose that you found this object somewhere and liked the way it worked but wanted to change its behavior so it could also write its output in color. The OBJECT2 program shows a preliminary version of how you might proceed. After completing this first take on creating a descendant of `TMyObject`, I will revisit the subject and show ways to improve the model shown here. The output from the program is shown in Figure 19.4.
FIGURE 19.4. The output from the OBJECT2 program.

In the old world of structured programming, the most likely step would be to rewrite the original `ShowHierarchy` method. However, rewriting an existing method can be a problem for two reasons:

- You might not have the source code to the routine, so you can't rewrite it. This is a common problem because most programming tools are delivered in binary libraries.

- You might have the source code but also know that this particular method is already being called by several different programmers. You, therefore, might be afraid to rewrite it because you might break the other programmers' code. Furthermore, making changes like this cuts you off from the upgrade path provided by the maker of the library. You can't just use the maker's next version of the product out of the box, because you now have a customized version of his or her library.

A combination of design and maintenance issues might deter the impulse to rewrite the original method. Many projects have been delayed or mothballed because changes in their designs have broken existing code and thrown the entire project into chaos.

OOP has a simple solution to this whole problem. Instead of declaring `TMyObject` as

    class TMyObject :public TObject

    {

    public:

      TMyObject() : TObject()  {}

      void ShowHierarchy();

      void PrintString(AnsiString S);

    };

thoughtful programmers declare it like this:

    class TMyObject :public TObject

    {

    public:

      TMyObject() : TObject()  {}

      void ShowHierarchy();

      virtual void PrintString(AnsiString S);

    };

The difference is that in the second example, the `PrintString` method is declared as `virtual`.

If `PrintString` is declared as `virtual`, you can override it in a descendant object, thereby changing the way the method works without ever changing the original version of the method. This means that all the other code that relies on the first version of the program continues to work, and yet you can rewrite the function for your own purposes. Furthermore, this technique would work even if you didn't have the source code for `TMyObject`! I will show you how this works in just a moment.

Some readers might have asked themselves earlier why I created the `PrintString` method in the first place. The answer hinges on the fact that iterating through a hierarchy of VCL objects can always be accomplished by the same algorithm. The same technique works for all VCL objects. But the act of printing information to the screen changes depending on your current circumstances. Are you in DOS? Are you in Windows? Do you want to use colors? Each of these circumstances calls for a different way of printing information to the screen. As a result, I separated the screen IO portion of `TMyObject` from the portion of the object that iterates through a hierarchy. Furthermore, there is no need to declare `ShowHierarchy` as `virtual`, but I must declare `PrintString` as `virtual`. The reasoning here is simply that `ShowHierarchy` does not need to change in descendants of the object, but `PrintString` will need to change. In particular, it will need to change so that it can write output in color. These types of considerations are part of a subject known as object design.

At this point, you might think that using virtual methods seems like an unnecessarily opaque solution to this problem. Wouldn't it have been simpler to add new methods to the inherited class? Then the user of this second class could call these new methods rather than the ones from the first instance of the class. There are three problems with this technique:

- It requires the user to memorize a whole slew of different method names that perform related but slightly different tasks. This is precisely what you have to do in structured programming, and it is exactly what you want to avoid. Instead, you want to have one name that applies to all similar methods of this family. For instance, if you have an `animal` object, you are going to have to make the `Walk` method `virtual` because a bird walks on two legs, and a cat walks on four. The implementation of `Walk` is different for each animal, so you need to declare the method `virtual`. When you are done, you can use `Cat->Walk()`, and the cat will walk properly. Conversely, you can use `Bird->Walk()`, and the bird will walk properly. It would be a mess if you had to use `Bird->WalkOnTwoLegs()` and `Cat->WalkOnFourLegs()`. This proliferation of similar but slightly different method names is exactly the kind of structured programming fiasco that OOP was designed to avoid. Instead of a bunch of similar names like `WalkOnTwoLegs`, `CrawlOnYourBelly`, `WalkOnFourLegs`, and `WalkOnOneHundredLegs`, you want to have just one word, such as `Walk`, that applies to a whole family of objects. In other words, each object in the family will implement the `walk` method differently.

- The second problem is that these objects call the `PrintString` method internally. If you did not declare the method as `virtual`, you would have to figure out some way for the `ShowHierarchy` method to know whether it should call the implementation of `PrintString` that is part of `TMyObject`, or whether it should call the implementation that is part of `THierarchy`. By declaring a method as `virtual`, this chore will be handled for you automatically. If you create an instance of `THierarchy`, `ShowHierarchy` will call `THierarchy->PrintString` automatically; and if you create an instance of `TMyObject`, it will call `TMyObject->PrintString`. This is the way OOP handles virtual methods, and it is one of the key concepts that makes this system work.

- The final, and best, reason for doing things this way has to do with polymorphism. As such, it may not be clear to all readers at this time, but I promise that it will make sense after you have read the polymorphism chapter. You can declare a pointer of type `TMyObject` that can be assigned to a pointer of type `THierarchy`. When you then call `TMyObject->PrintString()`, the `PrintString` method of `THierarchy` will be called even though the object instance is of type `TMyObject`. This would not occur if `PrintString` were not declared `virtual`. In fact, this behavior is what polymorphism is all about, and indeed, it is one of the cornerstones of OOP programming.

The word virtual is inherited from one class to the next. If a base class declares a method `virtual`, the descendants need not do so, because they will inherit the `virtual` declaration for a particular method from the base class. Delphi users take note, as this is the exact opposite of what happens in Object Pascal. I should add that it is generally considered bad form not to repeat the declaration in child objects, as you want to be sure the reader of your code can see at a glance how it is structured.

Here is a look at a stripped-down descendant of `TMyObject` that overrides the `PrintString` method:

    class THierarchy: public TMyObject

    {

      int FColor;

    public:

      THierarchy() : TMyObject() {}

      virtual void PrintString(AnsiString S);

      __property int Color={read=FColor,write=FColor};

    };

This declaration states that class `THierarchy` is a descendant of class `TMyObject` and that it overrides `PrintString`.

> NOTE: Delphi programmers beware! The Pascal object model performs the same chore by using the `override` directive. That's not the way C++ works. This is a major change between the new BCB code and the old Object Pascal techniques.

A first take on the new version of the `PrintString` method looks like this:

    void THierarchy::PrintString(AnsiString S)

    {

      char Temp[250];

      textcolor(FColor);

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

      cputs(Temp);

    }

This code depends on functionality from `Conio.h` that allows you to print strings to the screen in color.

You can see that a field called `FColor` has been added to this object: `THierarchy` now contains not only procedures but also data. One of the key aspects of class declarations is that they can contain both methods and data, so that you can bring all the code related to the `THierarchy` object together in one place. This is part of a concept called encapsulation, explained in the next chapter.

> NOTE: By convention, VCL objects declare private data as starting with the letter F, which stands for field. This technique is helpful, because it highlights the difference between an object's properties and its data. A property is published to the world and does not begin with the letter F. Private data is inaccessible to the rest of the world and begins with the letter F.

When you run the OBJECT2 program, the following code is executed in its main body:

    int main(void)

    {

      TMyObject *MyObject = new TMyObject();

      MyObject->ShowHierarchy();

      MyObject->Free();

      THierarchy *Hierarchy = new THierarchy();

      Hierarchy->Color = GREEN;

      Hierarchy->ShowHierarchy();

      Hierarchy->Free();

      getch();

      return 0;

    }

This code creates an object of type `THierarchy` and then shows you how to use the new functionality of the `ShowHierarchy` method. I also create an instance of `TMyObject` so that you can compare the two classes demonstrated so far in this chapter.

> NOTE: I should perhaps mention that it is more expensive to declare or call virtual methods than it is to call a static method. As a result, you need to weigh the whole issue of whether you want to declare a method to be `virtual`.
> In my opinion, you should usually create objects that have the best possible design, re-gardless of the amount of overhead they entail. Of course, it is possible to take this theory too far, but the mere fact of adding a few `virtual` methods is usually not the problem in bloated object hierarchies.
> Besides space and performance, a second reason for not declaring an object `virtual` is a desire to hide its implementation so you can change it later. There is usually no point in declaring a private method `virtual`, because it can't be seen by other objects, unless they are friends of the original object. (I will talk about friend objects later in this chapter.) The great advantage of private methods is that they can always be changed later to whatever degree you want, because other objects usually cannot see them and cannot access them directly. As a result, you may want to declare methods `private`, and non-`virtual`, so you can change their implementation later on. Needless to say, I am referring specifically to the act of changing the number of parameters these methods take.
> Your users will, of course, complain if you take a method they occasionally want to override and make it `private` and non-`virtual`. However, it is sometimes better to listen to their complaints than to saddle them with a broken object that cannot be fixed without breaking existing code.

In this section, you have learned about the `virtual` directive. This subject's true significance won't be clear until you read about polymorphism. However, before you tackle that subject, it's best to learn more about inheritance and encapsulation. In particular, the next section of the chapter looks at more issues involving object design.

#### Searching for the Right Design

It is almost impossible to find the right design for an object the first time you write it. As a rule, the only way you can figure out the design for an object is by creating it, discovering its limitations, and then making improvements to its design. In short, object design is an iterative process.

There is no good way to step you through the process of discovering the correct object design in a book, because the written word is by its nature static, and the process I'm describing is dynamic. Furthermore, it's confusing to the reader to show a series of poorly designed objects that are successively improved in each iteration. The problem with this technique is that the reader keeps seeing the wrong way to create an object and can easily pick up bad habits or fundamental misconceptions about object design. Even worse, the reader tends to get frustrated with having to unlearn the techniques they just acquired in the previous example that are now revealed as being flawed.

To avoid the problems outlined in the preceding paragraph, I will simply show you a second version of the `THierarchy` object and explain why it contains changes to the original version of the object you saw in the last section. This process does not tell you much about how I discovered the flaws in the object, but it will show you what the flaws are and how I got around them. The main point to grasp is that object design is an iterative process, and that the correct way to find the flaws in an object is to implement them once as best you can, and then look for problems.

> NOTE: There is a second school of thought that states that you can find the correct design for an object before implementing it. Proponents of this technique often suggest that a team be split in two, with part of the members designing objects and the other part implementing them. I have to confess that I've never actually discussed the results of this technique with someone who has used it successfully, as all my experience has been with programmers who use the iterative technique I discuss here. (Some of these programmers have also tried the second school of programming, but it did not work for them.)
> Of course, you should try to get an object right the first time, and you should use high-level tools that help with design. However, you should also expect to have to refine the objects you create through a perhaps lengthy, iterative process.
> Furthermore, you should design your object defensively. That is, you should carefully hide your implementation inside private methods and data because you will surely have to change its design at a later time.

The first problem with the original version of the `THierarchy` object became clear when I wanted to find a method to clear the screen. When doing so, I needed to first set the text and background color to which I wanted the screen to be cleared. As a result, I added new properties and new set methods to the object. The new property let me add a background color, and the set methods let me change the text and background colors at the same time I assigned them to the private data:

    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};

    };

The implementation of `PrintString` now looks like this:

    void THierarchy::PrintString(AnsiString S)

    {

      char Temp[250];

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

      cputs(Temp);

    }

Note that I have removed the code that changed the color of the text. This code is no longer necessary as the color gets changed in the `SetTextColor` method.

The extremely straightforward implementation of `ClrScr` looks like this:

    void THierarchy::ClrScr()

    {

      clrscr();

    }

> NOTE: C++ allows you to declare methods `inline`, as shown by the `SetTextColor` and `SetBackColor` method declarations. Conversely, you can also declare a method outside of the object, as I do in the case of `ClrScr`. This latter technique is called an `out_of_line` declaration, but that has such a ghastly ring in my ear that I refuse to use it. You can, in fact, implement a method outside an object and make it inline by using the `inline` directive:

>     inline void SetTextColor(int Color)

>     {

>       FTextColor = Color;

>       textcolor(FTextColor);

>     }

> Inline methods usually execute faster than regular methods because they are placed directly in your code and do not require the overhead associated with a function call. Whether you implement them inside or outside of a class declaration, you should declare only very small methods as `inline`, and they should not contain any loops.
> Inline methods are great, and I use them regularly. The only drawback I see to them is that they can make object declarations difficult to read. In particular, the great thing about an object declaration is that it can provide a summary of the functionality of an object without asking you to wade through its implementation. Inline methods implemented inside a class declaration detract from this feature because they clutter up the landscape.
> One possible workaround is to declare inline functions separately from the object declaration by using the `inline` keyword. In fact, this is probably the ideal solution, and it is only laziness that keeps me from using it at all times. I, for one, would have been glad if the compiler enforced this rule.

The next change I made to `THierarchy` involved a desire to increase the flexibility of the object. In particular, it would be great to be able to use this object even if you do not descend from it directly. One way to do this is via multiple inheritance, but that technology is not supported by VCL objects. I should perhaps add that I am not particularly partial to multiple inheritance as a technology for use in real-world applications.

A much simpler way to make the object more flexible is to pass the `ShowHierarchy` method a copy of the object whose hierarchy you want to explore:

    void TMyObject::ShowHierarchy(TObject *AnObject)

    {

      TClass AClass;

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

      PrintString(AClassName);

      AClass = AnObject->ClassParent();

      while (AClass)

      {

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

        PrintString(AClassName);

        AClass = AClass->ClassParent();

      }

    }

The interesting thing about this change is that it occurs at the level of the `TMyObject` implementation and declaration. In other words, it represents changes not to `THierarchy` but to `TMyObject`. If this object were of more earth-shaking import and if I had released `TMyObject` to the public, I could not have made this sort of change because I would be breaking the code of those who already called the `ShowHierarchy` method. There are, I suppose, three things you can learn from this example:

  1. Try not to publish any part of an object hierarchy until you are sure you can live with its current public interface.
  2. Hide your implementation. Avoid letting consumers of your objects directly call one of the methods that involves a significant part of your implementation.
  3. Object design is an art, not a science. There is no such thing as a perfect object. All objects have flaws. Plan them out carefully ahead of time, bring them to fruition by a lengthy iterative process, and then send them out into the world with the understanding that your design is flawed by definition. Recognize that you are going to have to support the objects you send into the world, because your users are going to find flaws in them. It is the mark of an amateur to stonewall the consumer of an object when he or she offers intelligent criticism of an object implementation. Likewise, it is naïve for the consumer of an object to expect it to be perfect. Perfection is too high a goal. Instead, shoot for high quality, and then demand that object producers provide fixes when flaws are discovered. Fixes need not arrive immediately, but they should appear in the next version of the product.

After making these changes, I decided that the object was ready to see the light of day. As a result, I moved it into its own file called `MyObject.cpp`. I then made a program called OBJECT3 that tests this new arrangement. The results of this effort are shown on the CD-ROM that accompanies this book in a program called OBJECT3. The code for this program is shown in Listings 19.3 through 19.5.
Listing 19.3. TMyObject and THierarchy now reside in their own file, called MyObject.

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

    // 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

    {

    public:

      TMyObject() : TObject()  {}

      void ShowHierarchy(TObject *AnObject);

      virtual void PrintString(AnsiString S);

    };

    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};

    };

    #endif

Listing 19.4. The implementation for TMyObject and THierarchy are shown here in MyObject.cpp.

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

    // 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 (AClass)

      {

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

        PrintString(AClassName);

        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();

    }

Listing 19.5. The test program for the MyObject unit.

    #include <vcl\vcl.h>

    #include <conio.h>

    #pragma hdrstop

    #include "myobject.h"

    USEUNIT("MyObject.cpp");

    void main()

    {

      THierarchy *H = new THierarchy();

      H->TextColor = YELLOW;

      H->BackColor = BLUE;

      H->ClrScr();

      H->ShowHierarchy(H);

      delete H;

      getch();

    }

Notice that I include code in the test program that "uses" the `MyObject` unit. I did not insert this code manually but instead used the program manager to add the `MyObject` module to the project. This is the best way to proceed, as it allows you to avoid editing the make file.

The program itself simply sets a background and text color for the output, then clears the screen to those colors and shows the hierarchy to the user. The next-to-last line in the program calls `delete` rather than `Free`. You can use either technique, depending on the dictates of your taste and background.

#### Showing the Hierarchy of a VCL Program

To really appreciate the new objects developed in the last section, you need to add them to a regular BCB application. For instance, if you use them in a standard `Form1` class, here is the output you get:

    TForm1

    TForm

    TScrollingWinControl

    TWinControl

    TControl

    TComponent

    TPersistent

    TObject

This shows the whole hierarchy of class `TForm1`, starting with `this`, moving to `TForm`, back to `TScrollingWinControl`, and so on, all the way back to `TObject`.

As you know, VCL objects do not support multiple inheritance. As a result, I could not add the functionality of `THierarchy` to `TForm` by letting the object inherit it. This is fine with me, because I use multiple inheritance only very reluctantly.

Instead of multiple inheritance, I prefer to use a technique called aggregation. In aggregation, you add the object you want to use as a field of your current object and then expose its methods either by publishing the whole object as a property, or by wrapping the methods of the object inside methods of your current object. This technique allows you to easily take precise control of the functionality from the new object, and it tends to support clean, bug-free programming.

Before I show you how to use aggregation, I need to point out that it is not possible to use `THierarchy` directly in a Windows program because `THierarchy` uses `Conio.h`. The question then becomes, "Is `THierarchy` designed in such a way that I can descend from it and change its functionality so that it works with a Windows program?"

Well, part of the answer should be obvious. The key method that had to be virtual, the one that had to be overridden to make this work, is `PrintString`. And indeed, `PrintString` is declared `virtual`, so it is possible to change `THierarchy`'s stripes. In fact, you will find that I also override the setters and the `ClrScr` method.

> NOTE: You might feel that having to override so many methods in order to change the output of this program is an excessive amount of work when compared to the relatively simple task of rewriting the `ShowHierarchy` method from scratch. Indeed, when looked at from this perspective, the whole task of creating an object in this case seems fruitless. I readily confess that it would indeed have been simpler to write three versions of a non-OOP method called `ShowHierarchyBlackandWhite`, `ShowHierarchyColor`, and `ShowHierarchyWindows`. Here is a `ShowHierarchy` method tailored for use with a VCL form-based program:

>     void TForm1::ShowHierarchy()

>     {

>       TClass AClass;

>       Memo1->Clear();

>       Memo1->Lines->Add(AnsiString(ClassName()));

>       AClass = ClassParent();

>       while (True)

>       {

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

>         Memo1->Lines->Add(S);

>         if (AnsiString(AClass->ClassName()) == "TObject")

>           break;

>         AClass = AClass->ClassParent();

>       }

>     }

> Clearly, this object is easier to write than the objects I have produced here. The key reason I created an object like `THierarchy` is simply that it illustrates how OOP works. In real life, objects often contain 20, 30, or even 50 methods. When seen in that light, having to override three or four methods does not seem like such a big chore. Furthermore, you can often use delegation to solve these kinds of problems.
> Of course, this isn't real life but a book on programming. I have therefore intentionally created a small object with few methods so that I can focus your attention solely on `virtual` methods that need to be overridden. If I had cluttered up this chapter with a big object, you never could have seen the trees for the forest, because at least half the chapter would have involved an explanation of a huge object.
> Furthermore, the `ShowHierarchy` method is the difficult method in this program. It's easy to write the methods that need to be overridden, while some programmers might have trouble creating `ShowHierarchy`. In this sense, even the rather simple object I have created here does a good job of hiding complexity and promoting bug-free reuse.
> A final point about this subject is that OOP's main strength is not in producing small code, nor is it true that objects are necessarily easier to create than equivalent structured code. Rather, the strength of OOP is in letting you easily and safely reuse premade objects. Now that these objects are completed, they can be reused easily in any WinTel-based C++ program, whether it runs in Windows or from the command line. OOP never makes sense if you think in terms of writing your whole program from scratch. Instead, the advantage of OOP comes when you want to create programs by reusing premade objects. OOP is about code reuse and about writing bug-free programs; it is not about finding the smallest possible implementation of an algorithm. When you add components to OOP, you can also get RAD, which is another major boost in productivity.

In Listings 19.6 through 19.9, you will find the code for the new version of the `Object1.cpp` module, as well as the code for a standard VCL form-based program called HIERARCHY that uses the object. The output from the program is shown in Figure 19.5.

######

FIGURE 19.5. The HIERARCHY form sports a `TButton` and a `TMemo` component.

Listing 19.6. The new code for the MyObject header file.

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

    // 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)

        { FList->Clear(); ShowHierarchy(AnObject); return FList; }

    };

    class __declspec(delphiclass) TVCLHierarchy;

    class THierarchy: public TMyObject

    {

      friend TVCLHierarchy;

      int FTextColor;

      int FBackColor;

    protected:

      virtual __fastcall void SetTextColor(int Color)

        { FTextColor = Color; textcolor(FTextColor); }

      virtual __fastcall 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 __fastcall void SetTextColor(int Color)

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

      virtual __fastcall void SetBackColor(int Color);

    public:

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

      virtual void PrintString(AnsiString S)

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

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

    };

    #endif

###

Listing 19.7. The new code for the MyObject implementation.

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

    // 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();

    }

    void __fastcall TVCLHierarchy::SetBackColor(int Color)

    {

      FBackColor = Color;

      FMemo->Color = TColor(FBackColor);

    }

###

Listing 19.8. The code for the HIERARCHY header file.

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

    // Main.h

    // Copyright (c) 1997 by Charlie Calvert

    //

    #ifndef MainH

    #define MainH

    #include <vcl\Classes.hpp>

    #include <vcl\Controls.hpp>

    #include <vcl\StdCtrls.hpp>

    #include <vcl\Forms.hpp>

    #include <vcl\ExtCtrls.hpp>

    #include "MyObject.h"

    class TForm1 : public TForm

    {

    __published:

      TButton *ShowHierarchyBtn;

      TMemo *Memo1;

      TPanel *Panel1;

      void __fastcall ShowHierarchyBtnClick(TObject *Sender);

      void __fastcall FormDestroy(TObject *Sender);

    private:

      TVCLHierarchy *FHierarchy;

      void ShowHierarchy(TObject *Object);

    public:

      virtual __fastcall TForm1(TComponent* Owner);

      __property TVCLHierarchy *Hierarchy=

        {read=FHierarchy, write=FHierarchy};

    };

    extern TForm1 *Form1;

    #endif

Listing 19.9. The code for the HIERARCHY program.

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

    // Main.cpp

    // Copyright (c) 1997 by Charlie Calvert

    //

    #include <vcl\vcl.h>

    #pragma hdrstop

    #include "Main.h"

    #pragma resource "*.dfm"

    TForm1 *Form1;

    __fastcall TForm1::TForm1(TComponent* Owner)

      : TForm(Owner)

    {

      FHierarchy = new TVCLHierarchy(Memo1);

      Hierarchy->BackColor = clBlue;

      Hierarchy->TextColor = clYellow;

    }

    void __fastcall TForm1::FormDestroy(TObject *Sender)

    {

      delete Hierarchy;

    }

    void TForm1::ShowHierarchy(TObject *Object)

    {

      Hierarchy->ShowHierarchy(Object);

    }

    void __fastcall TForm1::ShowHierarchyBtnClick(TObject *Sender)

    {

      ShowHierarchy(this);

    }

When you run this program, it will display the hierarchy of the `TForm` object in a memo control. It does so by creating an aggregate of the `THierarchy` object and the `TForm1` object.

#### Creating Friend Objects

Here is the declaration for the descendant of `THierarchy` that works in Windows:

    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(); }

    };

Several changes to this program should jump right out at you. First, notice that the constructor now takes a parameter. This parameter is the memo control that will be used to display text to the user. The `SetTextColor`, `SetBackColor`, `PrintString`, and `ClrScr` methods have all been rewritten to take advantage of this new control.

As a result, the only parts of the original object that have come through unchanged are as shown in the following pseudocode:

    class THierarchy: public TMyObject}

    {

      int FTextColor;

      int FBackColor;

    public:

      void ShowHierarchy(TObject *AnObject); // inherited from TMyObject

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

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

    };

In this particular case, it is not possible to use the `TextColor` and `BackColor` properties to access the private `FTextColor` and `FBackColor` data stores. Instead, I declare `TVCLHierarchy` to be a friend of `THierarchy`:

    class TVCLHierarchy;

    class THierarchy: public TMyObject

    {

      friend TVCLHierarchy;

      int FTextColor;

      int FBackColor;

      ... // Code omitted here

    }

Notice that `TVCLHierarchy` is declared as a friend in the first line of the `THierarchy` declaration. This means that `TVCLHierarchy` has access to the private data of `THierarchy`. Descendants of `TVCLHierarchy` will not inherit this trait.

#### Using Aggregation

Rather than use multiple inheritance to access `TVCLHierarchy`, `TForm1` declares a field of this type:

    class TForm1 : public TForm

    {

      ... // Code omitted here

      TVCLHierarchy *Hierarchy;

      void ShowHierarchy();

    public:

      virtual __fastcall TForm1(TComponent* Owner);

      __property TVCLHierarchy *Hierarchy=

        {read=FHierarchy, write=FHierarchy};

    };

I have also added a method called `ShowHierarchy` and a new property called `Hierarchy` so that descendants of `TForm1` can have access to the `BackColor` and `TextColor` properties of `TVCLHierarchy`.

A fine point of object design could be debated here. In this particular case, I have created a property of type `TVCLHierarchy` that is exposed to consumers of `TForm1`. Alternatively, I could have completely hidden `TVCLHierarchy` behind a set of properties that provided access to the `FTextColor` and `FBackColor` fields of `THierarchy`:

    class TForm1 : public TForm

    {

      ... // Code omitted here

      TVCLHierarchy *Hierarchy;

      void ShowHierarchy(TObject *Object);

      int GetHierarchyBackColor();

      void SetHierarchyBackColor(int Color);

      int GetHierarchyTextColor();

      void SetHierarchyTextColor(int Color);

    public:

      virtual __fastcall TForm1(TComponent* Owner);

      __property int HierarchyTextColor=

        {read=GetHierarchyTextColor, write=SetHierarchyTextColor};

      __property int HierarchyBackColor=

        {read=GetHierarchyBackColor, write=SetHierarchyBackColor};

    };

This technique is very pure from an OOP-based point of view, and it is perhaps a more classic and complete example of aggregation than the one I use. However, it is not strictly necessary to create the `HierarchyTextColor` and `HierarchyBackColor` properties. Instead, I can make the `Hierarchy` object public by making it a property of type `TVCLHierarchy`. The reason this approach is acceptable is because `FHierarchy`, as well as the `FTextColor` and `FBackColor` fields of `THierarchy`, are all still hidden behind properties.

It would be wrong, however, to make `FHierarchy` public or to allow users to directly access the `FTextColor` or `FBackColor` fields of `THierarchy`. The reason this is wrong is because it exposes your data to the public, thereby limiting your choices when it comes time to maintain or improve your program.

The technique shown here of exposing `TVCLHierarchy` via a property shows up everywhere in the VCL. Most notably, it is present in the `Items` or `Lines` fields of `TListBox`, `TComboBox`, and `TMemo`. The point here is that you want to use properties to protect your data and your implementation, but you don't want to take things so far that your code becomes needlessly bloated.

However, I do not feel that it would be incorrect or wrong to completely hide `FHierarchy` from consumers of `TForm1` and to instead expose its properties via properties of `TForm1`. The great advantage of this technique would be that it would allow a seamless aggregation of `TForm1` and `TVCLHierarchy`.

> NOTE: If you sense me waffling a bit here, that is because I do not believe there is a hard and fast answer as to what is best in a situation like this. It happens that there are good arguments on both sides. Component design is an art, not a science. The moment it becomes a science, most programmers are going to be out of a job.

In the constructor for `TForm1`, you should create the `TVCLHierarchy` object:

    __fastcall TForm1::TForm1(TComponent* Owner)

      : TForm(Owner)

    {

      FHierarchy = new TVCLHierarchy(Memo1);

      Hierarchy->BackColor = clBlue;

      Hierarchy->TextColor = clYellow;

    }

As you can see, I have chosen to hard code default values for the `BackColor` and `TextColor` properties of `TVCLHierarchy` into this constructor. This is not necessarily the best thing to do from the point of view of a descendant of `TForm1`, but it will not cause any problems in the simple example I am creating here.

At last, I am able to create a wrapper for the `ShowHierarchy` method:

    void TForm1::ShowHierarchy(TObject *Object)

    {

      Hierarchy->ShowHierarchy(Object);

    }

Once again, you could probably just as easily let consumers of `TForm1` access this method via the `Hierarchy` property. However, I provide this method in part because it is easier to use than a property, and in part because I want to illustrate explicitly how aggregation works.

Finally, I have included a method of `TForm1` that calls the `TForm1::ShowHierarchy` method:

    void __fastcall TForm1::ShowHierarchyBtnClick(TObject *Sender)

    {

      ShowHierarchy(this);

    }

This method does nothing to forward my example of aggregation, and I include it solely for expediency's sake. A real-world example of aggregation would leave `TForm1` without features of this type and would instead expect you to inherit from it in order to access the functionality exposed via `Hierarchy` and `ShowHierarchy`. For an example of this type of program, see the HIERARCHY2 program in the `Chap19` directory.

A reader could object that rather than using aggregation, I could have declared a single method that looked like this:

    void __fastcall TForm1::ShowHierarchyBtnClick(TObject *Sender)

    {

      THierarchy *Hierarchy = new TVCLHierarchy(Memo1);

      Hierarchy->BackColor = clBlue;

      Hierarchy->TextColor = clYellow;

      Hierarchy->ShowHierarchy();

      delete Hierarchy;

    }

Once again, I have to confess that this probably is a simpler technique in this particular case. However, if you created descendants of `TForm1`, aggregation would allow them to call the `ShowHierarchy` method with one line of code. It would, in effect, add the functionality of `ShowHierarchy` to `TForm`, so that `TForm1` appeared to be an "aggregate" of two objects. Admittedly, the simplicity of the example I show here makes these advantages difficult to discern, but when you are working with larger, more complex objects, this technology really starts to shine. OOP is about managing complexity, but it is easier to teach OOP if you use simple examples.

By this time, you should have a fairly good grasp of inheritance and hierarchies. The key point to understand is that, in general, a child object inherits the capability to use any of its parent's methods. In other words, it comes into an inheritance, where the inheritance is the methods, fields, and properties of its parent. Except for `TObject` itself, all VCL objects have parents that can trace their roots back to `TObject`.

#### Form Inheritance and Aggregation

Form inheritance is a powerful technique used by VCL programs. Here is a description of how to use it.

  1. In the HIERARCHY2 program, I remove the `ShowHierarchy` button from the `Form1` file used in the HIERARCHY program.
  2. I also rename `Form1` to `HierarchyForm1`. I then save the old `Form1` file as a new file called `HierarchyForm`.
  3. Next I start a new project and remove its main form.
  4. I add the `HierarchyForm` to this new project and use the Object Repository to create a new form that descends from `HierarchyForm1`. To do this, choose File | New, click on the HIERARCHY2 page in the Object Repository, and choose to inherit a new form from `HierarchyForm1`.
  5. Save the new form you have created as `Main.cpp`.
  6. Open the Options | Project menu and use the Forms page to set `Form1` as the main form for your application. In other words, make the descendant of the `HierarchyForm` the main form for your application.

When you are done, the declaration for your main form should look like this:

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

    // HierarchyForm.h

    // Copyright (c) 1997 by Charlie Calvert

    //

    #ifndef HierarchyFormH

    #define HierarchyFormH

    #include <vcl\Classes.hpp>

    #include <vcl\Controls.hpp>

    #include <vcl\StdCtrls.hpp>

    #include <vcl\Forms.hpp>

    #include <vcl\ExtCtrls.hpp>

    #include "Myobject.h"

    class THierarchyForm1 : public TForm

    {

    published:

      TMemo *Memo1;

      TPanel *Panel1;

      void __fastcall FormDestroy(TObject *Sender);

    private:

      TVCLHierarchy *FHierarchy;

    protected:

      void ShowHierarchy(TObject *Object);

    public:

      virtual __fastcall THierarchyForm1(TComponent* Owner);

      __property TVCLHierarchy *Hierarchy=

        {read=FHierarchy, write=FHierarchy};

    };

    extern THierarchyForm1 *HierarchyForm1;

    #endif

Go into the declaration for `HierarchyForm1` and make sure that `ShowHierarchy` is declared in the public or protected section.

Now add a button to the main form called `ShowHierarchy` and add the following event handler to its `OnClick` event:

    void __fastcall TForm1::ShowHierarchyBtnClick(TObject *Sender)

    {

      ShowHierarchy(this);

    }

Now run the program, and when you click on `ShowHierarchy`, you should get the following result:

    TForm1

    THierarchyForm1

    TForm

    TScrollingWinControl

    TWinControl

    TControl

    TComponent

    TPersistent

    TObject

As you can see, the hierarchy now goes from `TForm1` to `HierarchyForm1` to `TForm`, and so on. `TForm1` is descended from `THierarchyForm1`, and it therefore inherits the ability to all the things a regular form can do, plus it can show its own hierarchy.

Once again, there are a number of quibbles you could make here. For instance, it might be better to remove the visual controls from the `HierarchyForm`, and so on. However, the main point of this exercise is merely to illustrate how form inheritance works, and the code shown here does that.

For the sake of clarity, I have included the complete code to the program in Listings 19.10 through 19.14. Remember that you need to use the visual tools to inherit from the `HierarchyForm`, or `Form1` will not have the correct appearance.
Listing 19.10. The header for the HierarchyForm from the HIERARCHY2 program.

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

    // HierarchyForm.h

    // Copyright (c) 1997 by Charlie Calvert

    //

    #ifndef HierarchyFormH

    #define HierarchyFormH

    #include <vcl\Classes.hpp>

    #include <vcl\Controls.hpp>

    #include <vcl\StdCtrls.hpp>

    #include <vcl\Forms.hpp>

    #include <vcl\ExtCtrls.hpp>

    #include "Myobject.h"

    class THierarchyForm1 : public TForm

    {

    __published:

      TMemo *Memo1;

      TPanel *Panel1;

      void __fastcall FormDestroy(TObject *Sender);

    private:

      TVCLHierarchy *FHierarchy;

    protected:

      void ShowHierarchy(TObject *Object);

    public:

      virtual __fastcall THierarchyForm1(TComponent* Owner);

      __property TVCLHierarchy *Hierarchy=

        {read=FHierarchy, write=FHierarchy};

    };

    extern THierarchyForm1 *HierarchyForm1;

    #endif

Listing 19.11. The main form for the HierarchyForm.

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

    // HierarchyForm.cpp

    // Copyright (c) 1997 by Charlie Calvert

    //

    #include <vcl\vcl.h>

    #pragma hdrstop

    #include "HierarchyForm.h"

    #pragma resource "*.dfm"

    THierarchyForm1 *HierarchyForm1;

    __fastcall THierarchyForm1::THierarchyForm1(TComponent* Owner)

      : TForm(Owner)

    {

      FHierarchy = new TVCLHierarchy(Memo1);

      Hierarchy->BackColor = clBlue;

      Hierarchy->TextColor = clYellow;

    }

    void __fastcall THierarchyForm1::FormDestroy(TObject *Sender)

    {

      delete Hierarchy;

    }

    void THierarchyForm1::ShowHierarchy(TObject *Object)

    {

      Hierarchy->ShowHierarchy(Object);

    }

Listing 19.12. The header for the programs main form.

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

    // Main.h

    // Hierarchy2

    // Copyright (c) 1997 by Charlie Calvert

    //

    #ifndef MainH

    #define MainH

    #include <vcl\Classes.hpp>

    #include <vcl\Controls.hpp>

    #include <vcl\StdCtrls.hpp>

    #include <vcl\Forms.hpp>

    #include <vcl\ExtCtrls.hpp>

    #include "Myobject.h"

    #include "HierarchyForm.h"

    class TForm1 : public THierarchyForm1

    {

    __published:

      TButton *ShowHierarchyBtn;

      void __fastcall ShowHierarchyBtnClick(TObject *Sender);

    private:

    public:

      virtual __fastcall TForm1(TComponent* Owner);

    };

    extern TForm1 *Form1;

    #endif

Listing 19.13. The main form for the program.

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

    // Main.cpp

    // Hierarchy2

    // Copyright (c) 1997 by Charlie Calvert

    //

    #include <vcl\vcl.h>

    #pragma hdrstop

    #include "Main.h"

    #include "HierarchyForm.h"

    #pragma link "HierarchyForm"

    #pragma link "HierarchyForm"

    #pragma resource "*.dfm"

    TForm1 *Form1;

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

    __fastcall TForm1::TForm1(TComponent* Owner)

      : THierarchyForm1(Owner)

    {

    }

    void __fastcall TForm1::ShowHierarchyBtnClick(TObject *Sender)

    {

      ShowHierarchy(this);

    }

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

Listing 19.14. The project source for the HIERARCHY2 program.

    #include <vcl\vcl.h>

    #pragma hdrstop

    USERES("Hierarchy2.res");

    USEFORM("Main.cpp", Form1);

    USEFORM("HierarchyForm.cpp", HierarchyForm1);

    USEUNIT("..\..\Utils\MyObject.cpp");

    WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int)

    {

      try

      {

        Application->Initialize();

        Application->CreateForm(__classid(TForm1), &Form1);

        Application->CreateForm(__classid(THierarchyForm1), &HierarchyForm1);

        Application->Run();

      }

      catch (Exception &exception)

      {

        MessageBox(NULL, exception.Message.c_str(), Application->Title.c_str(),

           MB_OK);

      }

      return 0;

    }

I have included the complete source for this program so you can double-check your work against it in case anything goes wrong. The key parts you need to concentrate on are the class declarations in the headers, the declarations for the global variable for the `HierarchyForm`, and the headers for the various methods used in the forms. The actual implementation of the methods was already cleared up in the HIERARCHY program, so you don't need to check them. The HIERARCHY2 program is shown in Figure 19.6.
FIGURE 19.6. The HIERARCHY2 program at runtime.

You should spend some time adding controls to the `HierarchyForm` and watching how they then automatically appear on the main form for the program. Notice that if you move a control on the `HierarchyForm`, the corresponding control on the main form will also move. If you move a control on the main form, however, that breaks the connection between that control and the corresponding control on the `HierarchyForm`. To restore the connection, right-click on the control in the main form and choose Revert To Inherited from the menu.

You can disconnect and revert properties one at a time if you wish. For instance, if you move the left side of a component on the main form, that will break the connection for that one property, but it will leave the remaining properties still connected to the `HierarchyForm`. To restore the `Left` property, choose that property in the Object Inspector with the right mouse button and select Revert To Inherited from the menu. To test this, you might want to drop a single button in the middle of the `HierarchyForm`, then switch back to the main form and work on changing just one of the inherited button's properties, such as its `Left` property or its `Caption`.

### Summary

In this chapter, you have had a good chance to start working with object-oriented programming. However, there are still several big topics to tackle, including encapsulation and polymorphism. Rather than trying to cover such big topics inside this already lengthy chapter, I have decided to break things up and give them their own chapters where they can have plenty of room to unfold naturally.

Plenty of material was covered in this chapter, including inheritance, virtual methods, aggregation, and form inheritance. If you are new to this material, it would only be natural that some of it did not sink in the first time around. If so, you can either re-read the chapter or go on to the next chapter and see if some of it doesn't start to become clear when you work with new examples that approach the material from a slightly different angle.

Inheritance and virtual methods are everywhere in the BCB, and the more you work with them, the easier it will be to understand the principles involved. However, this is material that you must master if you want to be good at using BCB--and particularly if you want to start creating your own components.

©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.