📄 ch19.htm
字号:
<BR>
<A NAME="Heading8"></A><A HREF="19ebu02.jpg" tppabs="http://pbs.mcp.com/ebooks/0672310228/art/19/19ebu02.jpg">FIGURE 19.2.</A><FONT COLOR="#000077">
</FONT><I>The output from the first take of the OBJECT1 program as it appears
when
run from the DOS prompt.</I></P>
<P>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.</P>
<P>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.</P>
<P>To begin, you should create a small object at
the top of the program:</P>
<PRE><FONT COLOR="#0066FF">class TMyObject
{
};
int main(void)
{
return 0;
}
</FONT></PRE>
<P>All I have done here is added a simple class definition and removed the <TT>printf()</TT>statements.</P>
<P>Delphi
programmers should note that this class is not a descendant of <TT>TObject</TT>,
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 <TT>TObject</TT> or one of <TT>TObject</TT>'s children. The reason
for this rule is that <TT>TObject</TT> 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 <TT>TObject</TT>. However, you can create C++
objects that do not descend from <TT>TObject</TT> and thus do not include this intelligence.
<DL>
<DT></DT>
</DL>
<BLOCKQUOTE>
<P>
<HR>
<FONT
COLOR="#000077"><B>NOTE:</B></FONT><B> </B>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.
<HR>
</BLOCKQUOTE>
<P>To create a true VCL object, you should change <TT>TMyObject</TT>'s definition
so that it reads as follows:</P>
<PRE><FONT COLOR="#0066FF">#include <vcl\vcl.h>
class TMyObject :
public TObject
{
public:
__fastcall TMyObject(void) : TObject() {};
}
int main(void)
{
return 0;
}
</FONT></PRE>
<P>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 <TT>__published</TT>
directive.</P>
<P>All VCL objects must have a constructor, and it should be declared <TT>__fastcall</TT>.
Methods or functions declared <TT>__fastcall</TT> 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.</P>
<P>All VCL
objects that are descendants of <TT>TComponent</TT> must have destructors
declared <TT>__fastcall virtual</TT>:</P>
<PRE><FONT COLOR="#0066FF">__fastcall virtual TComponent(TComponent* Aowner);
</FONT></PRE>
<P>The reason for the <TT>__fastcall</TT>
and <TT>virtual</TT> restrictions has to
do with conformance to the VCL programming model. In particular, the VCL declares
the constructor for <TT>TComponent</TT> as <TT>virtual</TT>, so C++ objects that
descend from <TT>TComponent</TT> must follow
along. This means that all components
you create must be declared with <TT>virtual</TT> constructors, because all components
are, at least in practice, descendants of <TT>TComponent</TT>. I comment on this
fact simply because it is very unusual for
C++ constructors to be declared <TT>virtual</TT>.
<DL>
<DT></DT>
</DL>
<BLOCKQUOTE>
<P>
<HR>
<FONT COLOR="#000077"><B>NOTE:</B></FONT><B> </B>Actually, it is theoretically possible
for you to create your own class that performs the same chores
that <TT>TComponent</TT>
performs. There is nothing magical about <TT>TComponent</TT>; 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 <TT>TComponent</TT>. However, this is not strictly true, as you could create your
own class that appears on the Component
Palette without descending from <TT>TComponent</TT>.
I personally cannot imagine any set of circumstances that would justify the effort
involved in duplicating the work done in <TT>TComponent</TT>.
<HR>
</BLOCKQUOTE>
<P>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:</P>
<PRE><FONT COLOR="#0066FF">#include
<vcl\vcl.h>
class TMyObject : public TObject
{
public:
__fastcall TMyObject(void);
};
__fastcall TMyObject::TMyObject(void) : TObject()
{
}
int main(void)
{
return 0;
}
</FONT></PRE>
<P>When you implement a constructor,
you should follow the header with a colon and
a call to the ancestor's constructor:</P>
<PRE><FONT COLOR="#0066FF">_fastcall TMyObject::TMyObject(void) : TObject()
</FONT></PRE>
<P>Now that you have an overview of a basic VCL object declaration, the
next step
is to declare a variable of type <TT>TMyObject</TT> and then instantiate it and dispose
of it:</P>
<PRE><FONT COLOR="#0066FF">class TMyObject : public TObject
{
public:
__fastcall TMyObject() : TObject() {}
};
int main(void)
{
TMyObject *MyObject = new TMyObject;
delete MyObject;
return 0;
}
</FONT></PRE>
<P>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
<TT>TMyObject</TT>:</P>
<PRE><FONT COLOR="#0066FF">TMyObject *MyObject
</FONT></PRE>
<P>Next, it allocates the memory for the object:</P>
<PRE><FONT COLOR="#0066FF">new TMyObject;
</FONT></PRE>
<P>Put together on one line, the statement looks like
this:</P>
<PRE><FONT COLOR="#0066FF">TMyObject *MyObject = new TMyObject;
</FONT></PRE>
<P>This statement actually creates a pointer variable of type <TT>TMyObject</TT>.
In VCL programming, you have to take this step if you want to use
<TT>MyObject</TT>,
and, furthermore, you must dispose of this memory when you are finished with it.</P>
<P>There are two ways to destroy an object. One is to call <TT>Free</TT>, and the
other is to use the <TT>delete</TT> operator:</P>
<PRE><FONT
COLOR="#0066FF">MyObject->Free();
delete MyObject;
</FONT></PRE>
<P>Both techniques have the same outcome. I believe the majority of people prefer
<TT>delete</TT>, and it is what I use most often in the code found in this book.
However, there are
reasons you might want to call <TT>Free</TT>, so I will discuss
it in the next few paragraphs.</P>
<P>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
<TT>Free</TT> method
of <TT>TObject</TT>:</P>
<PRE><FONT COLOR="#0066FF">procedure TObject.Free;
begin
if Self <> nil then
Destroy;
end;
</FONT></PRE>
<P>The variable <TT>Self</TT> always points to the current object. It plays the
same
role in Object Pascal that <TT>this</TT> plays in C++. If you are inside one of the
methods of an object, you can refer to that object by using <TT>Self</TT>. (<TT>Self</TT>
is passed as an implicit parameter to all BCB methods.) Here is how the
VCL <TT>Free</TT>
method would look in C++:</P>
<PRE><FONT COLOR="#0066FF">void __fastcall TObject::Free()
{
if (this != NULL)
~TObject();
}
</FONT></PRE>
<BLOCKQUOTE>
<P>
<HR>
<FONT COLOR="#000077"><B>NOTE:</B></FONT><B> </B>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.
<HR>
</BLOCKQUOTE>
<P>Standard C++ does not define a <TT>Free</TT> 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 <TT>Free</TT>
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.</P>
<P>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.
<H3><A NAME="Heading12"></A><FONT COLOR="#000077">Understanding Inheritance</FONT></H3>
<P>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 <TT>private</TT>
directive can limit the capability of a child to call some of its parent's routines.
The <TT>private</TT> directive is explained in depth
later in this chapter.</P>
<P>Except for its constructor, all of <TT>TMyObject</TT>'s methods and fields are
inherited:</P>
<PRE><FONT COLOR="#0066FF">class TMyObject : public TObject
{
public:
__fastcall TMyObject() : TObject() {}
};
</FONT></PRE>
<P>This declaration is somewhat deceiving because <TT>TObject</TT> contains many
methods that are available to instances of <TT>TMyObject</TT>. In other words, <TT>TMyObject</TT>
is not quite as simple an object as it appears at
first.</P>
<P>So, what are all these methods associated with <TT>TObject</TT>? Well, you can
see their definitions, as well as their implementations, if you open up the <TT>SysDefs.h</TT>
file from the <TT>\BCB\Include\VCL </TT>subdirectory:</P>
<PRE><FONT COLOR="#0066FF">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);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -