📄 ch21.htm
字号:
can redefine the implementation of these methods. The key elements that define a
typical case of polymorphism are a base class and the descendants that inherit a
base class's methods. In
particular, the fanciest type of polymorphism involves virtual
methods that are inherited from a base class.</P>
<P>A classic example of polymorphism is evident if you examine the BCB VCL. All these
objects are descendants of a single base class
called <TT>TObject</TT>; therefore,
they all inherit a virtual destructor. As a result, you can pass all the many hundreds
of BCB classes to a routine that takes a parameter of the same type as their base
class:</P>
<PRE><FONT COLOR="#0066FF">void
FreeAllClasses(TObject O)
{
delete O;
}
</FONT></PRE>
<P>You can pass any VCL object to the <TT>FreeAllClasses</TT> function, and the object
will be properly destroyed. The VirtualMethodTest program, shown in Listings 21.1
and 21.2, shows how
this process works. You can also find this program on the CD
that comes with this book.<BR>
<BR>
<A NAME="Heading11"></A><FONT COLOR="#000077"><B>Listing 21.1. The header for the
VirtualMethodTest program.</B></FONT></P>
<PRE><FONT
COLOR="#0066FF">///////////////////////////////////////
// Main.h
// VirtualMethodTest
// 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>
class TParent
{
public:
TParent() {}
virtual __fastcall ~TParent()
{ ShowMessage("TParent Destructor"); }
virtual void Draw() {}
};
class TChild: public TParent
{
public:
TChild(): TParent() {}
virtual __fastcall ~TChild()
{ ShowMessage("TChild Destructor"); }
virtual void Draw() {}
virtual void ShowHierarchy() {}
};
class TForm1 : public TForm
{
__published:
TButton
*RunTestBtn;
void __fastcall RunTestBtnClick(TObject *Sender);
private:
void FreeObjects(TParent *Parent);
public:
virtual __fastcall TForm1(TComponent* Owner);
};
extern TForm1 *Form1;
#endif
</FONT></PRE>
<PRE><FONT COLOR="#0066FF">
</FONT></PRE>
<P><A NAME="Heading12"></A><FONT COLOR="#000077"><B>Listing 21.2. The main body of
the main form for the VirtualMethodTest program.</B></FONT></P>
<PRE><FONT COLOR="#0066FF">///////////////////////////////////////
// Main.cpp
//
VirtualMethodTest
// 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)
{
}
void TForm1::FreeObjects(TParent *Parent)
{
delete Parent;
}
void __fastcall TForm1::RunTestBtnClick(TObject *Sender)
{
TParent *Parent = new TParent();
TChild *Child = new TChild();
FreeObjects(Parent);
FreeObjects(Child);
}
</FONT></PRE>
<P>From a visual point of view, this program is very unexciting, as you can see from
a glance at Figure 21.1.<BR>
<BR>
<A NAME="Heading13"></A><A HREF="21ebu01.jpg" tppabs="http://pbs.mcp.com/ebooks/0672310228/art/21/21ebu01.jpg">FIGURE 21.1.</A><FONT COLOR="#000077">
</FONT><I>The main form of the VirtualMethodTest program.</I></P>
<P>To put this program to work, you need to change the destructors for the <TT>TParent</TT>
and <TT>TChild</TT> objects so that they are sometimes declared as virtual and sometimes
declared as standard, non-virtual methods:</P>
<PRE><FONT COLOR="#0066FF">class TParent
{
public:
TParent() {}
virtual __fastcall ~TParent() { ShowMessage("TParent Destructor"); }
virtual void Draw() {}
};
class TParent
{
public:
TParent() {}
__fastcall ~TParent() { ShowMessage("TParent Destructor"); }
virtual void Draw() {}
};
</FONT></PRE>
<P>In the first case, the destructor is declared as virtual, and in the second, it
is not declared as
virtual. Notice that I do not make <TT>TParent</TT> descend from
<TT>TObject</TT>, because all VCL classes must have virtual destructors since <TT>TObject</TT>
itself declares its destructor as virtual.</P>
<P>When you do not declare the destructors
as virtual, and you pass the objects to
the <TT>FreeObjects</TT> method, the destructor for <TT>TParent</TT> is called:</P>
<PRE><FONT COLOR="#0066FF">void TForm1::FreeObjects(TParent *Parent)
{
delete Parent;
}
</FONT></PRE>
<P>If you declare
both destructors as virtual, when you pass them to <TT>FreeObjects</TT>,
the destructor for <TT>TChild</TT> will be called, even though it is being called
off an instance of <TT>TParent</TT>.</P>
<P>If what I'm saying is not clear, start C++Builder
and load the <TT>VirtualMethodTest</TT>
program so that you can watch this process in action. Run the program with the destructor
declared as virtual and then run it again without the virtual destructor declaration.
This material is extremely
important. You simply cannot understand what OOP is all
about if you do not understand polymorphism.</P>
<P>Remember that passing a parameter to <TT>FreeAllClasses</TT> is analogous to an
assignment statement. In effect, you are writing</P>
<PRE><FONT
COLOR="#0066FF">Parent = Parent;
Parent = Child;
</FONT></PRE>
<P>You could not, however, write this:</P>
<PRE><FONT COLOR="#0066FF">void TForm1::FreeObjects(TChild *Child)
{
delete Child;
}
void __fastcall TForm1::Button1Click(TObject
*Sender)
{
TParent *Parent = new TParent();
TChild *Child = new TChild();
FreeObjects(Parent);
FreeObjects(Child);
}
</FONT></PRE>
<P>The issue, of course, is that by passing <TT>Parent</TT> to <TT>FreeAllClasses</TT>,
you are, in
effect, asking BCB to make the following assignment:</P>
<PRE><FONT COLOR="#0066FF">Child = Parent;
</FONT></PRE>
<P>BCB is kind enough to tell you that making this assignment is bad practice.</P>
<P>Every object in the VCL can use polymorphism to
some degree because they all inherit
methods from <TT>TObject</TT>. In particular, they all inherit a virtual destructor.
Without this virtual destructor, the whole system would collapse. In particular,
the component array hosted by each form is
iterated at closing time, and the destructor
of each object is called. Doing so would not be possible if the destructors of these
objects were not declared virtual. Or, rather, doing so would be possible, but the
objects themselves would not be
properly destroyed. The reason for the failure is
illustrated clearly in the VirtualMethodTest program.</P>
<P>To help illustrate this point, I will implement the classic shape demo example
described earlier in this chapter. This program creates a
parent object called <TT>TMyShape</TT>
and two child objects called <TT>TRectangle</TT> and <TT>TCircle</TT>. All three
of these objects have virtual methods called <TT>DrawShape</TT>. You can pass a single
instance of this object to a method declared
as follows:</P>
<PRE><FONT COLOR="#0066FF">void TForm1::CallDrawShape(TMyShape *Shape)
{
Shape->DrawShape();
}
</FONT></PRE>
<P>If you pass in an instance of <TT>TMyShape</TT>, then the <TT>TMyShape::DrawShape</TT>
method will be called,
which is what you would expect. However, if you pass in either
a <TT>TRectangle</TT> or <TT>TCircle</TT>, then the respective <TT>DrawShape</TT>
methods of each object will be called, as shown in Figure 21.2.<BR>
<BR>
<A NAME="Heading14"></A><A
HREF="21ebu02.jpg" tppabs="http://pbs.mcp.com/ebooks/0672310228/art/21/21ebu02.jpg">FIGURE 21.2.</A><FONT COLOR="#000077">
</FONT><I>The ClassicShapeDemo shows how one object can behave in three different
ways: on the left it draws a star, in the center a circle, and on the right a square.</I></P>
<P>The
<TT>TForm1::CallDrawShape</TT> method works with a variable of type <TT>TMyShape</TT>.
It has only one kind of object in it. It works only with variables of type <TT>TMyShape</TT>.
And yet if you pass both a <TT>TRectangle</TT> object and a
<TT>TMyShape</TT> object
into it, one instance will call the <TT>DrawShape</TT> method of <TT>TRectangle</TT>
and the other will call the <TT>DrawShape</TT> method of <TT>TMyShape</TT>. This
example illustrates polymorphism in action. The concept here
is so important that
I have placed this example on disk in a directory called <TT>ClassicShapeDemo</TT>
and show it in Listings 21.3 and 21.4.<BR>
<BR>
<A NAME="Heading15"></A><FONT COLOR="#000077"><B>Listing 21.3. The header for ClassicShapeDemo
shows how polymorphism looks in action.</B></FONT></P>
<PRE><FONT COLOR="#0066FF">///////////////////////////////////////
// Main.h
// Classic Shape Demo
// 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>
class TMyShape: public TCustomControl
{
public:
virtual __fastcall
TMyShape(TComponent *AOwner): TCustomControl(AOwner) {}
virtual __fastcall TMyShape(TComponent *AOwner, int X, int Y)
: TCustomControl(AOwner) { Width = 50; Height = 50; Left = X; Top = Y; }
virtual void DrawShape()
{
Canvas->Brush->Style = bsClear; Canvas->TextOut(0, 0, "*"); }
};
class TCircle: public TMyShape
{
public:
virtual __fastcall TCircle(TComponent *AOwner): TMyShape(AOwner) {}
virtual __fastcall TCircle(TComponent *AOwner,
int X, int Y)
: TMyShape(AOwner) { Width = 50; Height = 50; Left = X; Top = Y; }
virtual void DrawShape()
{ Canvas->Brush->Color = clBlue; Canvas->Ellipse(0, 0, Width, Height); }
};
class TRectangle: public TMyShape
{
public:
virtual __fastcall TRectangle(TComponent *AOwner): TMyShape(AOwner) {}
virtual __fastcall TRectangle(TComponent *AOwner, int X, int Y)
: TMyShape(AOwner) { Width = 50; Height = 50; Left = X; Top = Y; }
virtual void DrawShape()
{ Canvas->Brush->Color = clLime; Canvas->Rectangle(0, 0, Width, Height); }
};
class TForm1 : public TForm
{
__published:
TButton *DrawShapesBtn;
TPanel *Panel1;
void __fastcall DrawShapesBtnClick(TObject *Sender);
private:
void TForm1::DrawShape(TMyShape *Shape);
public:
virtual __fastcall TForm1(TComponent* Owner);
};
extern TForm1 *Form1;
#endif
</FONT></PRE>
<H3 ALIGN="CENTER"><FONT COLOR="#0066FF"></FONT></H3>
<P><A NAME="Heading16"></A><FONT
COLOR="#000077"><B>Listing 21.4. The main form for
the ClassicShapeDemo program.</B></FONT></P>
<PRE><FONT COLOR="#0066FF">///////////////////////////////////////
// Main.cpp
// Classic Shape Demo
// 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)
{
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -