📄 ch21.htm
字号:
}
void TForm1::DrawShape(TMyShape *Shape)
{
Shape->DrawShape();
}
void __fastcall TForm1::DrawShapesBtnClick(TObject *Sender)
{
TMyShape *Shape = new TMyShape(Panel1, 10, 10);
Shape->Parent = Panel1;
TCircle *Circle = new TCircle(Panel1, 50, 10);
Circle->Parent =
Panel1;
TRectangle *Rectangle = new TRectangle(Panel1, 140, 10);
Rectangle->Parent = Panel1;
DrawShape(Shape);
DrawShape(Circle);
DrawShape(Rectangle);
}
</FONT></PRE>
<P>Most of the complexity of this example is a result of
the default behavior of
VCL objects. Notice that <TT>TMyShape</TT> descends from <TT>TCustomControl</TT>.
I chose this lineage because <TT>TCustomControl</TT> has a <TT>Canvas</TT> object
ready for use by its consumers. I pay a price for this
functionality, because I must
create a default constructor with the following signature if I want to suppress valid
compiler warnings:</P>
<PRE><FONT COLOR="#0066FF">virtual __fastcall TMyShape(TComponent *AOwner): TCustomControl(AOwner) {}
</FONT></PRE>
<P>After I have declared this constructor, I can create a second constructor that
suits my own needs:</P>
<PRE><FONT COLOR="#0066FF">virtual __fastcall TMyShape(TComponent *AOwner, int X, int Y)
: TCustomControl(AOwner) { Width = 50;
Height = 50; Left = X; Top = Y; }
</FONT></PRE>
<P>Notice that this constructor gives the object a default height and width, and
also assigns values to its <TT>Left</TT> and <TT>Top</TT> fields. All these fields
are inherited from
<TT>TCustomControl</TT> and must be filled out properly if you
want to draw the object on the screen.
<DL>
<DT></DT>
</DL>
<BLOCKQUOTE>
<P>
<HR>
<FONT COLOR="#000077"><B>NOTE:</B></FONT><B> </B><TT>TCustomControl</TT> is one of
the objects from
which you can make descendants if you want to create a component.
In fact, both of the examples shown here are valid components that could be placed
on the Component Palette with only a little more work. However, I will save a complete
explanation
of <TT>TCustomControl</TT> for the next chapter of this book. For now,
all you need to know is that these fairly sophisticated objects come replete with
<TT>Left</TT>, <TT>Top</TT>, <TT>Width</TT>, and <TT>Height</TT> properties, as well
as an
easy-to-use <TT>Canvas</TT> object.
<HR>
</BLOCKQUOTE>
<P>When you create an instance of a VCL control, you need to give it both an owner
and a parent:</P>
<PRE><FONT COLOR="#0066FF">TCircle *Circle = new TCircle(Panel1, 50, 10);
Circle->Parent = Panel1;
</FONT></PRE>
<P>This code assigns <TT>Panel1</TT> as both the parent and owner of <TT>TCircle</TT>.
<TT>Panel1</TT> is just a standard panel to serve as the drawing surface on which
the program paints its output.</P>
<P>After you declare constructor objects of type <TT>TMyShape</TT>, <TT>TRectangle</TT>,
and <TT>TCircle</TT>, the final step is to pass each of these objects to the <TT>CallDrawShape</TT>
method:</P>
<PRE><FONT COLOR="#0066FF">CallDrawShape(Shape);
CallDrawShape(Circle);
CallDrawShape(Rectangle);
</FONT></PRE>
<P>When you look at it from this end, the fact that <TT>CallDrawShape</TT> would
produce different results depending on the type of object you pass into it makes
sense. However, if you
look at the <TT>CallDrawShape</TT> method itself, the fact
that you can pass objects of different types to it is not at all obvious:</P>
<PRE><FONT COLOR="#0066FF">void TForm1::CallDrawShape(TMyShape *Shape)
{
Shape->DrawShape();
}
</FONT></PRE>
<P>The odd thing about polymorphism is that you can make the assignment of an object
of type <TT>TRectangle</TT> to an object of type <TT>TMyShape</TT>. Once you understand
that you can do that, everything else falls out fairly
naturally.</P>
<P>If you have any questions about what's going on in this code, run this program.
Step through it with the debugger. Do whatever is necessary to make sense of this
very important code. The key point to grasp is that one variable,
called <TT>Shape</TT>,
which is of type <TT>TMyShape</TT>, behaves in three different ways. It's polymorphic--one
variable acting in different ways.
<H3><A NAME="Heading18"></A><FONT COLOR="#000077">Polymorphism in the VCL</FONT></H3>
<P>One place
where BCB uses polymorphism heavily is with the <TT>TField</TT> objects
assigned to the <TT>Fields</TT> array in a <TT>TTable</TT> object. The objects in
the <TT>Fields</TT> array are of type <TT>TField</TT>, but they behave as if they
are of type
<TT>TStringField</TT>, <TT>TIntegerField</TT>, <TT>TFloatField</TT>,
and so on. The issue, of course, is that variables of type <TT>TStringField</TT>
and <TT>TIntegerField</TT> can all be assigned to the <TT>Fields</TT> array because
you can assign a
child object to a variable of its parent type:</P>
<PRE><FONT COLOR="#0066FF">Parent = Child;
</FONT></PRE>
<P>Here is the declaration of the <TT>Fields</TT> property from the <TT>TDataSet</TT>
declaration in <TT>DB.hpp</TT>:</P>
<PRE><FONT
COLOR="#0066FF">__property TField* Fields[int Index] = {read=GetField, write=SetField};
</FONT></PRE>
<P>Clearly, this array of objects is of type <TT>TField</TT>. Suppose, however, that
you execute the following method in a simple program containing
a <TT>TTable</TT>
object that's pointed to the <TT>Biolife</TT> table:</P>
<PRE><FONT COLOR="#0066FF">void __fastcall TForm1::bViewFieldTypesClick(TObject *Sender)
{
int i;
for (i = 0; i < Table1->FieldCount; i++)
{
ListBox1->Items->Add(Table1->Fields[i]->ClassName());
}
}
</FONT></PRE>
<P>According to the declaration of the <TT>Fields</TT> object, the type of each of
the members of the <TT>Fields</TT> array should be <TT>TField</TT>. However,
the
following is what actually gets printed in the program's list box, as you can see
in Figure 21.3.</P>
<PRE><FONT COLOR="#0066FF">TFloatField
TStringField
TStringField
TStringField
TFloatField
TFloatField
TMemoField
TGraphicField
</FONT></PRE>
<P>Clearly, this isn't really an array of <TT>TField</TT> objects at all. So what's
going on anyway?<BR>
<BR>
<A NAME="Heading19"></A><A HREF="21ebu03.jpg" tppabs="http://pbs.mcp.com/ebooks/0672310228/art/21/21ebu03.jpg">FIGURE 21.3.</A><FONT COLOR="#000077">
</FONT><I>The PolyFields program
shows how polymorphism underlies key parts of the
VCL.</I></P>
<P>Well, by now the answer should be clear. Somewhere internally, BCB is assigning
<TT>TStringField</TT>, <TT>TFloatField</TT>, <TT>TMemoField</TT>, and <TT>TGraphicField</TT>
objects to
the members of the <TT>TField</TT> array. Why is this legal? Because setting
a parent object equal to a child object is always legal! In essence, the following
happens:</P>
<PRE><FONT COLOR="#0066FF">{
TField *Field;
TStringField *StringField;
Field = StringField; // This is legal!
}
</FONT></PRE>
<P>Here is the hierarchy of some of the key <TT>TField</TT> descendants:</P>
<PRE><FONT COLOR="#0066FF">-TField
-TStringField
-TNumericField
-TSmallIntField
-TWordField
-TAutoIncField
-TFloatField
-TCurrencyField
-TBlobField
-TMemoField
-TGraphicField
</FONT></PRE>
<P>Given this hierarchy, assigning a <TT>TStringField</TT> or <TT>TFloatField</TT>
object to a variable of type <TT>TField</TT> is
always legal:</P>
<PRE><FONT COLOR="#0066FF">{
TField *Field[3];
TStringField *StringField;
TFloatField *FloatField;
TGraphicField *GraphicField;
Fields[0] = StringField; // legal!
Fields[1] = FloatField; // legal!
Fields[2]
= GraphicField; // legal!
};
</FONT></PRE>
<P>This point is so important that I'm going to include the source code for a program
called <TT>PolyFlds.mak</TT> that demonstrates the issue discussed in this section.
Listings 21.5 and 21.6 show this
source code.<BR>
<BR>
<A NAME="Heading20"></A><FONT COLOR="#000077"><B>Listing 21.5. The PolyFields program
shows how BCB makes practical use of polymorphism.</B></FONT></P>
<PRE><FONT COLOR="#0066FF">#ifndef MainH
#define MainH
#include
<vcl\Classes.hpp>
#include <vcl\Controls.hpp>
#include <vcl\StdCtrls.hpp>
#include <vcl\Forms.hpp>
#include <vcl\DBGrids.hpp>
#include "Grids.hpp"
#include <vcl\DBTables.hpp>
#include
<vcl\DB.hpp>
class TForm1 : public TForm
{
__published:
TDBGrid *DBGrid1;
TButton *bViewFieldTypes;
TListBox *ListBox1;
TTable *Table1;
TDataSource *DataSource1;
void __fastcall bViewFieldTypesClick(TObject *Sender);
private:
public:
virtual __fastcall TForm1(TComponent* Owner);
};
extern TForm1 *Form1;
#endif
</FONT></PRE>
<P><A NAME="Heading21"></A><FONT COLOR="#000077"><B>Listing 21.6. The main form for
the PolyFields program.</B></FONT></P>
<PRE><FONT COLOR="#0066FF">#include <vcl\vcl.h>
#pragma hdrstop
#include "Main.h"
#pragma link "Grids"
#pragma resource "*.dfm"
TForm1 *Form1;
__fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
{
}
void __fastcall TForm1::bViewFieldTypesClick(TObject *Sender)
{
int i;
for (i = 0; i < Table1->FieldCount; i++)
{
ListBox1->Items->Add(Table1->Fields[i]->ClassName());
}
}
</FONT></PRE>
<P>This sample
program makes clear how important polymorphism is to BCB. The <TT>Fields</TT>
array of <TT>TTable</TT> is one of the key elements in all of BCB. And what lies
at the heart of the whole thing? Polymorphism!</P>
<P>Another important point to grasp here
is that very little about BCB is mysterious.
If you work with Visual Basic or PowerBuilder, the only explanation for much of the
syntax is simply "because." Why does it work that way? Because! With BCB,
the explanation is never like that.
You can find an explanation for everything in
BCB, and you can build any part of the VCL or the environment yourself by using BCB.</P>
<P>Remember: BCB is built into the VCL. You'll find no mysteries here. If you know
the language well enough, even
the most esoteric parts of the VCL will make sense
to you. How can the <TT>Fields</TT> array be so powerful? How does it do those things?
Well, now you know the answer: It's polymorphic!
<H3><A NAME="Heading22"></A><FONT COLOR="#000077">Polymorphism
Encapsulated (Review
of Main Points)</FONT></H3>
<P>I have, at long last, said all I want to say about polymorphism. The key points
to remember are these:
<UL>
<LI>You can set a parent equal to a child object, but you can't set a child equal
to a
parent object:
</UL>
<BLOCKQUOTE>
<PRE><FONT COLOR="#0066FF">Parent = Child; // Little set equal to big: OK
Child = Parent; // Big set equal to little: Doesn't work</FONT></PRE>
</BLOCKQUOTE>
<DL>
<DD>Setting a parent equal to a child
object is what makes polymorphism tick.
</DL>
<UL>
<LI>The defining elements in polymorphism are the methods of the parent object, particularly
those methods that are declared virtual. Even if you assign a child to a parent object
</UL>
<BLOCKQUOTE>
<PRE><FONT COLOR="#0066FF">Parent = Child;</FONT></PRE>
</BLOCKQUOTE>
<DL>
<DD>the parent can't call methods of the child that are not also visible in the parent's
class declaration. For example, if you assign a <TT>TForm</TT>
instance to a <TT>TObject</TT>
instance, you can't access the form's <TT>Caption</TT> property from the <TT>TObject</TT>
instance without a typecast.
</DL>
<P>The point is that you can take a whole slew of hierarchically arranged objects,
assign
each of them to their parent, call a virtual method belonging to the parent,
and watch them all behave in different ways. Polymorphism!</P>
<P>For some readers, I'm sure this information is old hat. Other readers might be
new to the subject but have
grasped it completely from the descriptions given already.
However, most readers probably still have some questions lingering in the backs of
their minds.</P>
<P>If you want, just reread the preceding sections as often as necessary. I know
from
personal experience that as many as three out of four object-oriented programmers
don't really understand polymorphism. The subject, however, is not that complicated.
Concentrate on these two sentences:</P>
<P>Polymorphism allows you to set one
variable of type TParent equal to a series
of child objects. When you call certain virtual methods of that parent, it will behave
in different ways, depending on the traits of the currently selected child.</P>
<P>That's a mouthful, but the concepts
set forth are not impossible to comprehend.</P>
<P>So far, all the examples of polymorphism are very stripped-down programs that
contain only code directly relevant to the subject at hand. However, the real power
of polymorphism won't become clear
until you work with a larger program that really
taps into the power of OOP. Unfortunately, the example I have for this kind of programming
really cries out to be implemented as a set of polymorphic components. Because I
have not yet described how to
create components, I will have to delay showing you
this program until Chapter 23, "Creating Components from Scratch." In that
chapter, you will find a sample program called WareHouse that clearly illustrates
what polymorphism looks like in
a large, and fairly practical, program.
<H3><A NAME="Heading23"></A><FONT COLOR="#000077">Summary</FONT></H3>
<P>In this chapter, you have tackled the complex subject of polymorphism. You saw
that polymorphism is built on the fact that OOP languages
enable you to assign a
variable of type child to a variable declared to be of type parent. When you then
call a method of the parent type, it will go to the address of the child object's
methods. As a result, an object of type <TT>TParent</TT>, when
assigned to four different
descendant objects, might react in four different ways. One object, many different
faces: polymorphism.</P>
<P ALIGN="CENTER"><A HREF="index-3.htm" tppabs="http://pbs.mcp.com/ebooks/0672310228/index.htm"><IMG SRC="toc.gif" tppabs="http://pbs.mcp.com/ebooks/0672310228/buttonart/toc.gif" WIDTH="41" HEIGHT="41"
ALIGN="BOTTOM" ALT="TOC"
BORDER="0" NAME="toc8"></A><A HREF="ch20.htm" tppabs="http://pbs.mcp.com/ebooks/0672310228/ch20.htm"><IMG SRC="back-1.gif" tppabs="http://pbs.mcp.com/ebooks/0672310228/buttonart/back.gif"
WIDTH="41" HEIGHT="41" ALIGN="BOTTOM" ALT="BACK" BORDER="0" NAME="toc6"></A><A HREF="ch22.htm" tppabs="http://pbs.mcp.com/ebooks/0672310228/ch22.htm"><IMG
SRC="forward.gif" tppabs="http://pbs.mcp.com/ebooks/0672310228/buttonart/forward.gif" WIDTH="41" HEIGHT="41" ALIGN="BOTTOM"
ALT="FORWARD" BORDER="0"
NAME="toc7"></A></P>
<P>
<P ALIGN="CENTER"><FONT COLOR="#000000">©</FONT><A HREF="copy.htm" tppabs="http://pbs.mcp.com/ebooks/0672310228/copy.htm">Copyright</A><FONT
COLOR="#000000">, Macmillan Computer Publishing. All rights reserved.</FONT>
</BODY>
</HTML>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -