📄 ch20.htm
字号:
Creating computerized representations of physical objects is a very common use of
OOP, but it is not the primary reason for the existence of this technology.
The great
benefits of OOP are related to the reuse, design, and maintenance of software. Using
OOP to represent real-world objects is only one branch of this field of knowledge.
<HR>
</BLOCKQUOTE>
<P>Note that object hierarchies always move
from the general to the specific:</P>
<PRE><FONT COLOR="#0066FF">TWidget
TSiliconChip
TPentiumChip
</FONT></PRE>
<P>The underlying logic is simple enough:
<DL>
<DD><B>1.</B> A <TT>TWidget</TT> could be almost any object that is bought and
sold.<BR>
<BR>
<B>2.</B> A <TT>TSiliconChip</TT> is some kind of silicon-based entity.<BR>
<BR>
<B>3.</B> A <TT>TPentiumChip</TT> is a specific kind of computer chip that has a
real-world counterpart.
</DL>
<P>The movement is from the abstract
to the specific. It is almost always a mistake
to embody specific traits of an object in a base class for a hierarchy. Instead,
these early building blocks should be so broad in scope that they can serve as parents
to a wide variety of related objects
or tools.</P>
<P>The main reason for this rule might not become apparent to all readers until they
have seen Chapter 21. The condensed explanation, however, looks like this:
<UL>
<LI><TT>TWidget</TT> encapsulates certain basic functionality that
applies to all
widgets. For instance, all widgets might keep track of the date they were created,
whereas only certain types of widgets might support the MX instruction set. Therefore,
you can use polymorphism to ask all your widgets certain basic
questions, such as
when they were created. Conversely, there are certain types of questions you can
ask only of widgets that are also computer processors. This system would not work
if you did not move from the general to the specific. In short,
you want all widgets
to have certain basic functionality, and then as you move up the hierarchy, you can
provide specific domains of knowledge or functionality to particular types of objects.
</UL>
<P>The Widget1 program found on the CD that
accompanies this book includes a declaration
for class <TT>TWidget</TT>. (See Listing 20.1.) This declaration appears in a file
called <TT>Widgets.h</TT>. <TT>TWidget</TT> descends from the VCL class called <TT>TCustomControl</TT>
and uses a
descendant of <TT>THierarchy</TT> through aggregation. As a result, you
will need to add both <TT>MyObject</TT> and the new Widgets files to your projects.</P>
<P>I have created a new file called Widgets because I am building a new type of object
that
is distinct from <TT>THierarchy</TT> and <TT>TMyObject</TT>. I could, of course,
have put all the objects in one file, but I thought it made more sense to separate
the different types of objects into different files. There is no hard-and-fast rule
governing this kind of decision, and you can take whichever course makes sense to
you on a case-by-case basis.</P>
<P>I have also added a new class to <TT>TMyObject</TT>:</P>
<PRE><FONT COLOR="#0066FF">class TListHierarchy: public TMyObject
{
private:
TStringList *FList;
protected:
virtual void PrintString(AnsiString S)
{ FList->Add(S); }
public:
TListHierarchy() { FList = new TStringList(); }
__fastcall virtual ~TListHierarchy() { delete FList; }
TStringList
*GetHierarchy(TObject *AnObject)
{ ShowHierarchy(AnObject); return FList; }
};
</FONT></PRE>
<P>As you can see, this class stores its information in a <TT>TStringList</TT>. You
can retrieve the list from the class by using the
<TT>GetHierarchy</TT> method:</P>
<PRE><FONT COLOR="#0066FF">ListBox1->Items = MyWidget->GetHierarchy();
</FONT></PRE>
<P>After you make this call, the list box referenced in the code would contain an
object hierarchy.
<H4><A
NAME="Heading9"></A><FONT COLOR="#000077">TWidget and Its Destructor</FONT></H4>
<P>The Widgets unit is very simple at this point. To create it, I went to the files
menu and chose New Unit. I then saved the file as <TT>Widgets.cpp</TT> and edited
the
header so that it looked like this:</P>
<PRE><FONT COLOR="#0066FF">///////////////////////////////////////
// Widgets.h
// Learning how to use objects
// Copyright (c) 1997 by Charlie Calvert
//
#ifndef WidgetsH
#define WidgetsH
#include
"myobject.h"
class TWidget: public TCustomControl
{
private:
TListHierarchy *Hierarchy;
public:
virtual __fastcall TWidget(TComponent *AOwner): TCustomControl(AOwner)
{ Hierarchy = new TListHierarchy; }
virtual __fastcall
~TWidget()
{ delete Hierarchy; }
TStringList *GetHierarchy()
{ return Hierarchy->GetHierarchy(this); }
};
#endif
</FONT></PRE>
<P>This object uses aggregation to enlist the functionality of the <TT>TListHierarchy</TT>
object for its
own purposes. In particular, it declares the object as private data
and then allocates memory for it in its constructor, and deallocates memory in its
destructor:</P>
<PRE><FONT COLOR="#0066FF">virtual __fastcall ~TWidget()
{ delete Hierarchy; }
</FONT></PRE>
<BLOCKQUOTE>
<P>
<HR>
<FONT COLOR="#000077"><B>NOTE:</B></FONT><B> </B>A destructor is declared by writing
the name of the object with a tilde prefaced to it: <TT>~TWidget</TT>. You rarely
have reason to call a destructor
explicitly. Instead, a destructor is called automatically
when you use the <TT>delete</TT> operator on the entire object, when you call Free,
or when a local object goes out of scope:</P>
<PRE><FONT COLOR="#0066FF">delete MyWidget; // Automatically
calls the destructor</FONT></PRE>
<P>Destructors exist so you can have a place to deallocate any memory associated
with an object, or to perform any other cleanup chores. The destructor exists for
your convenience and serves no other purpose than
to give you a chance to clean up
before your object shuts down. <BR>
<BR>
All VCL objects inherit a virtual destructor declared in <TT>TObject</TT>. If you
create a destructor for one of your own VCL objects, it must have the same signature
used
by the destructor in the <TT>TWidget</TT> object. That is, it must be declared
as virtual <TT>__fastcall</TT>. This is because of the precedent set by the destructor
found in <TT>TObject</TT>.
<HR>
</BLOCKQUOTE>
<P>The <TT>GetHierarchy</TT>
method of <TT>TWidget</TT> returns the result of a call
to the <TT>Hierarchy::GetHierarchy</TT> method:</P>
<PRE><FONT COLOR="#0066FF">TStringList *GetHierarchy()
{ return Hierarchy->GetHierarchy(this); }
</FONT></PRE>
<P>As you can see, this
function hardcodes a reference to the <TT>TWidget</TT> object
into this call. You therefore can't use this version of the <TT>Widget</TT> object
to get the hierarchy of another object, but only for retrieving its own hierarchy.</P>
<P>To make sure you
understand how I got started with this new series of files, I
have created a very simple program called Widget1 that contains the <TT>Widgets</TT>
unit and a main program that uses it. The code for the program is available on this
book's CD, and the
output from the program is shown in Figure 20.1.<BR>
<BR>
<A NAME="Heading11"></A><A HREF="20ebu01.jpg" tppabs="http://pbs.mcp.com/ebooks/0672310228/art/20/20ebu01.jpg">FIGURE 20.1.</A><FONT COLOR="#000077">
</FONT><I>The output from the Widget1 program found on the CD that accompanies this
book.</I></P>
<P>The main program contains one procedure that looks like this:</P>
<PRE><FONT COLOR="#0066FF">void __fastcall TForm1::ShowHierarchyBtnClick(TObject *Sender)
{
Memo1->Clear();
TWidget *W = new TWidget(Memo1);
Memo1->Lines =
W->GetHierarchy();
delete W;
}
</FONT></PRE>
<P>If you have questions on how to create this program, go to the <TT>Widget1</TT>
directory and study the example provided there. After you are set up, you can read
the note below and then copy the
<TT>Widgets.cpp</TT> and <TT>Widgets.h</TT> files
to the <TT>Utils</TT> directory, where <TT>CodeBox</TT> is kept. This file is used
in <TT>Widgets2</TT>, which is described in the next section of this chapter.</P>
<BLOCKQUOTE>
<P>
<HR>
<FONT
COLOR="#000077"><B>NOTE:</B></FONT><B> </B>Actually, you might want to be careful
copying the files. If you are following along by creating copies of the files by
hand, then go ahead and copy the files. If you are working from my source, be careful
that you don't overwrite my copy of the file when you move the file to the <TT>Utils</TT>
directory.
<HR>
</BLOCKQUOTE>
<H4><A NAME="Heading13"></A><FONT COLOR="#000077">Working with Widgets</FONT></H4>
<P>In the last section, you got started
with the <TT>TWidget</TT> object. In the
Widget2 program, shown in Listings 20.1 through 20.5, I add data and methods to the
object so it can serve as the base class for an object that represents some kind
of widget such as a computer chip, a light
bulb, a book, or whatever. Because the
object could have such a wide range of uses, I keep it very abstract, giving it only
a few traits such as a cost, a creation time, the ability to draw itself, and the
ability to stream itself.
<DL>
<DT></DT>
</DL>
<BLOCKQUOTE>
<P>
<HR>
<FONT COLOR="#000077"><B>NOTE:</B></FONT><B> </B>You will find that the Widgets module
gets rewritten several times over the course of the next few chapters. As a result,
the version of this unit used in the Widget2
program is stored in the <TT>Widgets2</TT>
directory on the CD that accompanies this book. There is a second version of this
unit, stored in the <TT>Utils</TT> subdirectory, that contains the final version
of <TT>Widgets.cpp</TT>. <BR>
<BR>
If
you are working along with this text, creating your own version of the program,
it might be best to copy <TT>Widgets.cpp</TT> out to the <TT>Utils</TT> directory
and update it little by little as the next few chapters unfold. If your efforts get
fouled for one reason or another, you can always revert to the versions stored on
the CD.
<HR>
</BLOCKQUOTE>
<P><A NAME="Heading15"></A><FONT COLOR="#000077"><B>Listing 20.1. The Main unit for
the Widget2 program.</B></FONT></P>
<PRE><FONT
COLOR="#0066FF">///////////////////////////////////////
// Main.cpp
// Learning about objects
// Copyright (c) 1997 by Charlie Calvert
//
#include <vcl\vcl.h>
#pragma hdrstop
#include "widgets.h"
#include "Main.h"
#pragma resource "*.dfm"
TForm1 *Form1;
__fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
{
}
void __fastcall TForm1::CreateBtnClick(TObject *Sender)
{
TWidget * Widget= new TWidget(this);
Widget->Left =
ClientWidth / 4 - Widget->Width / 2;
Widget->Top = (ClientHeight / 2) + Widget->Height;
Widget->Parent = this;
Widget->Cost = 3.3;
Widget->Description = "This is a widget";
Widget->TimeCreated = Now();
Memo1->Lines = Widget->GetHierarchy();
Widget->Show();
Edit1->Text = Widget->TimeCreated;
WriteWidgetToStream("Afile.dat", Widget);
}
void __fastcall TForm1::ReadFromStreamBtnClick(TObject *Sender)
{
TWidget
*Widget = ReadWidgetFromStream("AFile.dat");
Widget->Parent = this;
Memo1->Lines = Widget->GetHierarchy();
Widget->Show();
Edit1->Text = Widget->TimeCreated;
}
void __fastcall TForm1::FormResize(TObject
*Sender)
{
Memo1->Left = ClientWidth / 2;
}
</FONT></PRE>
<P><A NAME="Heading16"></A><FONT COLOR="#000077"><B>Listing 20.2. The core of the
Widget2 program is the Widgets unit. The header for that module is shown here.</B></FONT></P>
<PRE><FONT COLOR="#0066FF">///////////////////////////////////////
// Widgets.h
// Learning how to use objects
// Copyright (c) 1997 by Charlie Calvert
//
#ifndef WidgetsH
#define WidgetsH
#include "myobject.h"
class
__declspec(delphiclass) TWidget;
namespace Widgets
{
void __fastcall Register();
}
TWidget *ReadWidgetFromStream(AnsiString StreamName);
void WriteWidgetToStream(AnsiString StreamName, TWidget *Widget);
class TWidget: public TCustomControl
{
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -