⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 ch19.htm

📁 好书《C++ Builder高级编程技术》
💻 HTM
📖 第 1 页 / 共 5 页
字号:
  AClass = ClassParent();

  while (AClass)

  {

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

    PrintString(AClassName);

    AClass = AClass->ClassParent();

  }

}



</FONT></PRE>
<P>This code first writes 
the <TT>ClassName</TT> of the current object, which is
<TT>TMyObject</TT>. Then it gets the <TT>ClassParent</TT>, which is <TT>TObject</TT>,
and writes its name to the screen. The code then tries to get <TT>TObject</TT>'s
parent and fails, because 
<TT>TObject</TT> has no parent. At this point, <TT>AClass</TT>
is set to <TT>NULL</TT> and the code exits the <TT>while</TT> loop. The output for
the program is shown in Figure 19.3.<BR>
<BR>
<A NAME="Heading15"></A><A HREF="19ebu03.jpg" tppabs="http://pbs.mcp.com/ebooks/0672310228/art/19/19ebu03.jpg">FIGURE 
19.3.</A><FONT COLOR="#000077">
</FONT><I>The output from the OBJECT1 program.</I></P>
<P>In this section, you have learned about the <TT>Create</TT>, <TT>Destroy</TT>,
<TT>Free</TT>, <TT>ClassParent</TT>, and <TT>ClassName</TT> methods of 
<TT>TObject</TT>.
The declaration of <TT>TObject</TT> 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 (<TT>InheritsFrom</TT>) 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.
<H3><A NAME="Heading16"></A><FONT COLOR="#000077">Virtual Methods</FONT></H3>
<P>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, &quot;Polymorphism.&quot; Polymorphism is a relatively
difficult subject to grasp; 
therefore, I have stretched out a full explanation of
it over several chapters.</P>
<P>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.


<DL>
	<DT></DT>
</DL>



<BLOCKQUOTE>
	<P>
<HR>
<FONT COLOR="#000077"><B>NOTE:</B></FONT><B> </B>Delphi programmers should note that
	C++ does not support either the dynamic or message directives. In their place, you
	can use the <TT>MESSAGE_MAP</TT> 
macro. 
<HR>


</BLOCKQUOTE>

<P>The OBJECT2 program (shown in Listing 19.2) has one <TT>virtual</TT> method. The
<TT>virtual</TT> 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 <TT>PrintString</TT> as <TT>virtual</TT> and by creating a
descendant of <TT>TMyObject</TT> called <TT>THierarchy</TT>. 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 <TT>EInOutError</TT> exception. After changing
the setting, you should also rebuild your project so the new option takes effect.</P>


<BLOCKQUOTE>
	<P>
<HR>
<FONT 
COLOR="#000077"><B>NOTE:</B></FONT><B> </B>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. 
<HR>


</BLOCKQUOTE>

<P><A 
NAME="Heading19"></A><FONT COLOR="#000077"><B>Listing 19.2. The source code
for the main unit in the OBJECT2 program.</B></FONT></P>
<PRE><FONT COLOR="#0066FF">///////////////////////////////////////

// Object2.cpp

// Project: Object2

// Copyright 
(c) 1997 by Charlie Calvert

//

#include &lt;vcl/vcl.h&gt;

#include &lt;stdio.h&gt;

#include &lt;conio.h&gt;



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(&quot;%s\n&quot;, S.c_str());

}



void TMyObject::ShowHierarchy()

{

  TClass AClass;



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

  PrintString(AClassName);



  AClass = 
ClassParent();

  while (AClass)

  {

    AClassName = AnsiString(AClass-&gt;ClassName());

    PrintString(AClassName);

    AClass = AClass-&gt;ClassParent();

  }

}



void THierarchy::PrintString(AnsiString S)

{

  char Temp[250];



  
textcolor(FColor);

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

  cputs(Temp);

}



int main(void)

{

  TMyObject *MyObject = new TMyObject();

  MyObject-&gt;ShowHierarchy();

  MyObject-&gt;Free();



  THierarchy *Hierarchy = new 
THierarchy();

  Hierarchy-&gt;Color = YELLOW;

  Hierarchy-&gt;ShowHierarchy();

  Hierarchy-&gt;Free();



  getch();

  return 0;

}

</FONT></PRE>
<P>In OBJECT1, the <TT>ShowHierarchy</TT> 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 <TT>TMyObject</TT>, 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.<BR>
<BR>
<A NAME="Heading20"></A><A HREF="19ebu04.jpg" tppabs="http://pbs.mcp.com/ebooks/0672310228/art/19/19ebu04.jpg">FIGURE 
19.4.</A><FONT COLOR="#000077">
</FONT><I>The output from the OBJECT2 program.</I></P>
<P>In the old world of structured programming, the most likely step would be to rewrite
the original <TT>ShowHierarchy</TT> method. However, rewriting an existing 
method
can be a problem for two reasons:

<UL>
	<LI>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.
	<P>
	<LI>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.
</UL>

<P>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.</P>
<P>OOP has a simple 
solution to this whole problem. Instead of declaring <TT>TMyObject</TT>
as</P>
<PRE><FONT COLOR="#0066FF">class TMyObject :public TObject

{

public:

  TMyObject() : TObject()  {}

  void ShowHierarchy();

  void PrintString(AnsiString S);

};


</FONT></PRE>
<P>thoughtful programmers declare it like this:</P>
<PRE><FONT COLOR="#0066FF">class TMyObject :public TObject

{

public:

  TMyObject() : TObject()  {}

  void ShowHierarchy();

  virtual void PrintString(AnsiString S);

};


</FONT></PRE>
<P>The difference is that in the second example, the <TT>PrintString</TT> method
is declared as <TT>virtual</TT>.</P>
<P>If <TT>PrintString</TT> is declared as <TT>virtual</TT>, 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 <TT>TMyObject</TT>! I will show you how this works
in just a moment.</P>
<P>Some readers might have asked themselves earlier why I created the 
<TT>PrintString</TT>
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 <TT>TMyObject</TT> from the portion of the object that iterates through a hierarchy.
Furthermore, there is no need to declare <TT>ShowHierarchy</TT> as <TT>virtual</TT>,
but I must declare <TT>PrintString</TT> as 
<TT>virtual</TT>. The reasoning here is
simply that <TT>ShowHierarchy</TT> does not need to change in descendants of the
object, but <TT>PrintString</TT> 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.</P>
<P>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:

<UL>
	<LI>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 <TT>animal</TT> object, you are going to have to make the
	<TT>Walk</TT> method <TT>virtual</TT> because a bird walks on two legs, and a cat
	walks on four. The implementation of <TT>Walk</TT> is 
different for each animal,
	so you need to declare the method <TT>virtual</TT>. When you are done, you can use
	<TT>Cat-&gt;Walk()</TT>, and the cat will walk properly. Conversely, you can use
	<TT>Bird-&gt;Walk()</TT>, and the bird will walk 
properly. It would be a mess if
	you had to use <TT>Bird-&gt;WalkOnTwoLegs()</TT> and <TT>Cat-&gt;WalkOnFourLegs()</TT>.
	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 <TT>WalkOnTwoLegs</TT>, <TT>CrawlOnYourBelly</TT>,
	<TT>WalkOnFourLegs</TT>, and <TT>WalkOnOneHundredLegs</TT>, you want to have just
	one word, such as <TT>Walk</TT>, that applies to a 
whole family of objects. In other
	words, each object in the family will implement the <TT>walk</TT> method differently.

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -