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

📄 ch19.htm

📁 好书《C++ Builder高级编程技术》
💻 HTM
📖 第 1 页 / 共 5 页
字号:
	<P>
	<LI>The second problem is that these objects call the <TT>PrintString</TT> method
	internally. If you did not declare the 
method as <TT>virtual</TT>, you would have
	to figure out some way for the <TT>ShowHierarchy</TT> method to know whether it should
	call the implementation of <TT>PrintString</TT> that is part of <TT>TMyObject</TT>,
	or whether it should call the 
implementation that is part of <TT>THierarchy</TT>.
	By declaring a method as <TT>virtual</TT>, this chore will be handled for you automatically.
	If you create an instance of <TT>THierarchy</TT>, <TT>ShowHierarchy</TT> will call
	
<TT>THierarchy-&gt;PrintString</TT> automatically; and if you create an instance
	of <TT>TMyObject</TT>, it will call <TT>TMyObject-&gt;PrintString</TT>. This is the
	way OOP handles virtual methods, and it is one of the key concepts that makes this
	
system work.
	<P>
	<LI>The final, and best, reason for doing things this way has to do with polymorphism.
	As such, it may not be clear to all readers at this time, but I promise that it will
	make sense after you have read the polymorphism chapter. 
You can declare a pointer
	of type <TT>TMyObject</TT> that can be assigned to a pointer of type <TT>THierarchy</TT>.
	When you then call <TT>TMyObject-&gt;PrintString()</TT>, the <TT>PrintString</TT>
	method of <TT>THierarchy</TT> will be called even 
though the object instance is of
	type <TT>TMyObject</TT>. This would not occur if <TT>PrintString</TT> were not declared
	<TT>virtual</TT>. In fact, this behavior is what polymorphism is all about, and indeed,
	it is one of the cornerstones of OOP 
programming.
</UL>

<P>The word virtual is inherited from one class to the next. If a base class declares
a method <TT>virtual</TT>, the descendants need not do so, because they will inherit
the <TT>virtual</TT> declaration for a particular method 
from the base class. Delphi
users take note, as this is the exact opposite of what happens in Object Pascal.
I should add that it is generally considered bad form not to repeat the declaration
in child objects, as you want to be sure the reader of 
your code can see at a glance
how it is structured.</P>
<P>Here is a look at a stripped-down descendant of <TT>TMyObject</TT> that overrides
the <TT>PrintString</TT> method:</P>
<PRE><FONT COLOR="#0066FF">class THierarchy: public TMyObject

{

  int 
FColor;

public:

  THierarchy() : TMyObject() {}

  virtual void PrintString(AnsiString S);

  __property int Color={read=FColor,write=FColor};

};

</FONT></PRE>
<P>This declaration states that class <TT>THierarchy</TT> is a descendant of class

<TT>TMyObject</TT> and that it overrides <TT>PrintString</TT>.

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



<BLOCKQUOTE>
	<P>
<HR>
<FONT COLOR="#000077"><B>NOTE:</B></FONT><B> </B>Delphi programmers beware! The Pascal
	object model performs the same chore by using the 
<TT>override</TT> directive. That's
	not the way C++ works. This is a major change between the new BCB code and the old
	Object Pascal techniques. 
<HR>


</BLOCKQUOTE>

<P>A first take on the new version of the <TT>PrintString</TT> method looks like

this:</P>
<PRE><FONT COLOR="#0066FF">void THierarchy::PrintString(AnsiString S)

{

  char Temp[250];

  textcolor(FColor);

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

  cputs(Temp);

}

</FONT></PRE>
<P>This code depends on functionality from 
<TT>Conio.h</TT> that allows you to print
strings to the screen in color.</P>
<P>You can see that a field called <TT>FColor</TT> has been added to this object:
<TT>THierarchy</TT> now contains not only procedures but also data. One of the key
aspects 
of class declarations is that they can contain both methods and data, so
that you can bring all the code related to the <TT>THierarchy</TT> object together
in one place. This is part of a concept called encapsulation, explained in the next
chapter.


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



<BLOCKQUOTE>
	<P>
<HR>
<FONT COLOR="#000077"><B>NOTE:</B></FONT><B> </B>By convention, VCL objects declare
	private data as starting with the letter F, which stands for field. This technique
	is helpful, because it highlights 
the difference between an object's properties and
	its data. A property is published to the world and does not begin with the letter
	F. Private data is inaccessible to the rest of the world and begins with the letter
	F. 
<HR>


</BLOCKQUOTE>


<P>When you run the OBJECT2 program, the following code is executed in its main body:</P>
<PRE><FONT COLOR="#0066FF">int main(void)

{

  TMyObject *MyObject = new TMyObject();

  MyObject-&gt;ShowHierarchy();

  MyObject-&gt;Free();

  THierarchy 
*Hierarchy = new THierarchy();

  Hierarchy-&gt;Color = GREEN;

  Hierarchy-&gt;ShowHierarchy();

  Hierarchy-&gt;Free();

  getch();

  return 0;

}

</FONT></PRE>
<P>This code creates an object of type <TT>THierarchy</TT> and then shows you how
to 
use the new functionality of the <TT>ShowHierarchy</TT> method. I also create
an instance of <TT>TMyObject</TT> so that you can compare the two classes demonstrated
so far in this chapter.

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



<BLOCKQUOTE>
	<P>
<HR>
<FONT 
COLOR="#000077"><B>NOTE:</B></FONT><B> </B>I should perhaps mention that it
	is more expensive to declare or call virtual methods than it is to call a static
	method. As a result, you need to weigh the whole issue of whether you want to declare
	a 
method to be <TT>virtual</TT>. <BR>
	<BR>
	In my opinion, you should usually create objects that have the best possible design,
	re-gardless of the amount of overhead they entail. Of course, it is possible to take
	this theory too far, but the mere 
fact of adding a few <TT>virtual</TT> methods is
	usually not the problem in bloated object hierarchies. <BR>
	<BR>
	Besides space and performance, a second reason for not declaring an object <TT>virtual</TT>
	is a desire to hide its implementation so 
you can change it later. There is usually
	no point in declaring a private method <TT>virtual</TT>, because it can't be seen
	by other objects, unless they are friends of the original object. (I will talk about
	friend objects later in this chapter.) 
The great advantage of private methods is
	that they can always be changed later to whatever degree you want, because other
	objects usually cannot see them and cannot access them directly. As a result, you
	may want to declare methods 
<TT>private</TT>, and non-<TT>virtual</TT>, so you can
	change their implementation later on. Needless to say, I am referring specifically
	to the act of changing the number of parameters these methods take. <BR>
	<BR>
	Your users will, of course, 
complain if you take a method they occasionally want
	to override and make it <TT>private</TT> and non-<TT>virtual</TT>. However, it is
	sometimes better to listen to their complaints than to saddle them with a broken
	object that cannot be fixed 
without breaking existing code. 
<HR>


</BLOCKQUOTE>

<P>In this section, you have learned about the <TT>virtual</TT> directive. This subject's
true significance won't be clear until you read about polymorphism. However, before
you tackle that 
subject, it's best to learn more about inheritance and encapsulation.
In particular, the next section of the chapter looks at more issues involving object
design.
<H4><A NAME="Heading24"></A><FONT COLOR="#000077">Searching for the Right 
Design</FONT></H4>
<P>It is almost impossible to find the right design for an object the first time
you write it. As a rule, the only way you can figure out the design for an object
is by creating it, discovering its limitations, and then making 
improvements to its
design. In short, object design is an iterative process.</P>
<P>There is no good way to step you through the process of discovering the correct
object design in a book, because the written word is by its nature static, and the

process I'm describing is dynamic. Furthermore, it's confusing to the reader to show
a series of poorly designed objects that are successively improved in each iteration.
The problem with this technique is that the reader keeps seeing the wrong way to

create an object and can easily pick up bad habits or fundamental misconceptions
about object design. Even worse, the reader tends to get frustrated with having to
unlearn the techniques they just acquired in the previous example that are now revealed

as being flawed.</P>
<P>To avoid the problems outlined in the preceding paragraph, I will simply show
you a second version of the <TT>THierarchy</TT> object and explain why it contains
changes to the original version of the object you saw in the last 
section. This process
does not tell you much about how I discovered the flaws in the object, but it will
show you what the flaws are and how I got around them. The main point to grasp is
that object design is an iterative process, and that the correct 
way to find the
flaws in an object is to implement them once as best you can, and then look for problems.

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



<BLOCKQUOTE>
	<P>
<HR>
<FONT COLOR="#000077"><B>NOTE:</B></FONT><B> </B>There is a second school of thought
	that 
states that you can find the correct design for an object before implementing
	it. Proponents of this technique often suggest that a team be split in two, with
	part of the members designing objects and the other part implementing them. I have
	to 
confess that I've never actually discussed the results of this technique with
	someone who has used it successfully, as all my experience has been with programmers
	who use the iterative technique I discuss here. (Some of these programmers have also
	
tried the second school of programming, but it did not work for them.) <BR>
	<BR>
	Of course, you should try to get an object right the first time, and you should use
	high-level tools that help with design. However, you should also expect to have to
	
refine the objects you create through a perhaps lengthy, iterative process. <BR>
	<BR>
	Furthermore, you should design your object defensively. That is, you should carefully
	hide your implementation inside private methods and data because you will 
surely
	have to change its design at a later time. 
<HR>


</BLOCKQUOTE>

<P>The first problem with the original version of the <TT>THierarchy</TT> object
became clear when I wanted to find a method to clear the screen. When doing so, I
needed to 
first set the text and background color to which I wanted the screen to
be cleared. As a result, I added new properties and new set methods to the object.
The new property let me add a background color, and the set methods let me change
the text and 
background colors at the same time I assigned them to the private data:</P>
<PRE><FONT COLOR="#0066FF">class THierarchy: public TMyObject

{

  int FTextColor;

  int FBackColor;

protected:

  virtual void SetTextColor(int Color)

    { FTextColor = 
Color; textcolor(FTextColor); }

  virtual void SetBackColor(int Color)

    { FBackColor = Color; textbackground(FBackColor); }

public:

  THierarchy() : TMyObject() {}

  virtual void PrintString(AnsiString S);

  virtual void ClrScr();

  
__property int TextColor={read=FTextColor,write=SetTextColor};

  __property int BackColor={read=FBackColor,write=SetBackColor};

};

</FONT></PRE>
<P>The implementation of <TT>PrintString</TT> now looks like this:</P>
<PRE><FONT COLOR="#0066FF">void 
THierarchy::PrintString(AnsiString S)

{

  char Temp[250];

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

  cputs(Temp);

}

</FONT></PRE>
<P>Note that I have removed the code that changed the color of the text. This code
is no longer necessary as 
the color gets changed in the <TT>SetTextColor</TT> method.</P>
<P>The extremely straightforward implementation of <TT>ClrScr</TT> looks like this:</P>
<PRE><FONT COLOR="#0066FF">void THierarchy::ClrScr()

{

  clrscr();

}

</FONT></PRE>



<BLOCKQUOTE>
	<P>
<HR>
<FONT COLOR="#000077"><B>NOTE:</B></FONT><B> </B>C++ allows you to declare methods
	<TT>inline</TT>, as shown by the <TT>SetTextColor</TT> and <TT>SetBackColor</TT>
	method declarations. Conversely, you can also declare a method 
outside of the object,
	as I do in the case of <TT>ClrScr</TT>. This latter technique is called an <TT>out_of_line</TT>
	declaration, but that has such a ghastly ring in my ear that I refuse to use it.
	You can, in fact, implement a method outside an 
object and make it inline by using
	the <TT>inline</TT> directive:</P>
	<PRE><FONT COLOR="#0066FF">inline void SetTextColor(int Color)

{

  FTextColor = Color;

  textcolor(FTextColor);

}</FONT></PRE>
	<P>Inline methods usually execute faster than 
regular methods because they are placed
	directly in your code and do not require the overhead associated with a function
	call. Whether you implement them inside or outside of a class declaration, you should
	declare only very small methods as 
<TT>inline</TT>, and they should not contain any
	loops. <BR>
	<BR>
	Inline methods are great, and I use them regularly. The only drawback I see to them
	is that they can make object declarations difficult to read. In particular, the great
	thing 
about an object declaration is that it can provide a summary of the functionality
	of an object without asking you to wade through its implementation. Inline methods
	implemented inside a class declaration detract from this feature because they 
clutter
	up the landscape. <BR>
	<BR>
	One possible workaround is to declare inline functions separately from the object
	declaration by using the <TT>inline</TT> keyword. In fact, this is 

⌨️ 快捷键说明

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