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

📄 ch08.htm

📁 C++ From Scratch: An Object-Oriented Approach is designed to walk novice programmers through the ana
💻 HTM
📖 第 1 页 / 共 4 页
字号:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2//EN"><HTML><HEAD><SCRIPT LANGUAGE="JavaScript"><!--function popUp(pPage) { var fullURL = document.location; var textURL = fullURL.toString(); var URLlen = textURL.length; var lenMinusPage = textURL.lastIndexOf("/"); lenMinusPage += 1; var fullPath = textURL.substring(0,lenMinusPage); popUpWin = window.open('','popWin','resizable=yes,scrollbars=no,width=525,height=394'); figDoc= popUpWin.document; zhtm= '<HTML><HEAD><TITLE>' + pPage + '</TITLE>'; zhtm += '</head>'; zhtm += '<BODY bgcolor="#FFFFFF">'; zhtm += '<IMG SRC="' + fullPath + pPage + '">'; zhtm += '<P><B>' + pPage + '</B>'; zhtm += '</BODY></HTML>'; window.popUpWin.document.write(zhtm); window.popUpWin.document.close(); // Johnny Jackson 4/28/98 }//-->                                                                </SCRIPT>	<META NAME="Author" Content="Steph Mineart">	<META HTTP-EQUIV="Content-Type" CONTENT="text/html;CHARSET=iso-8859-1">	<TITLE>C++ From Scratch -- Ch 8 -- Using Polymorphism</TITLE><link rel="stylesheet" href="/includes/stylesheets/ebooks.css"></head><BODY TEXT="#000000" BGCOLOR="#FFFFFF"><CENTER><H1><IMG SRC="../button/que.gif" WIDTH="171" HEIGHT="66" ALIGN="BOTTOM" BORDER="0"><BR>C++ From Scratch</H1></CENTER><CENTER>  <P><A HREF="../index.htm"><IMG SRC="../button/contents.gif" WIDTH="128"HEIGHT="28" ALIGN="BOTTOM" ALT="Contents" BORDER="0"></A>   <HR></CENTER><H1 align="center">8</H1><H1 align="center"> <a name="_Toc447529655"></a>Using Polymorphism</H1><ul>  <li><a href="#Heading1">In this Chapter</a>   <li><a href="#Heading2">Specialization</a>     <ul>      <li><a href="#Heading3">Benefits from Specialization</a>       <li><a href="#Heading4">Polymorphism </a>       <li><a href="#Heading5">Abstract Data Types</a>       <li><a href="#Heading6">How This Is Implemented in C++</a>       <li><a href="#Heading7">The Syntax of Inheritance</a>     </ul>  <li><a href="#Heading8">Overriding Functions</a>   <li><a href="#Heading9">Virtual Methods</a>     <ul>      <li><a href="#Heading10">How Virtual Functions Work</a>       <li><a href="#Heading11">Virtual Destructors</a>     </ul>  <li><a href="#Heading12">Implementing Polymorphism</a>     <ul>      <li><a href="#Heading13">Adding a Second Letter</a>       <li><a href="#Heading14">Appending 'b'.Examining operator[]</a>     </ul></ul><hr size=4><h2><a name="_Toc450556044"></a><a name="_Toc447529654"></a> <a name="Heading1">In   this Chapter</a></h2><ul>  <li>     <p> Specialization</p>  </li>  <p></p>  <li>     <p> Overriding Functions</p>  </li>  <p></p>  <li>     <p> Virtual Methods</p>  </li>  <p></p>  <li>     <p> Implementing Polymorphism</p>  </li></ul><p>The linked list class works, but it cries out for a bit of improvement. Each   node in the list is forever checking to see whether there is a next node in   the list before taking action:</p><pre><tt>void Node::Insert(char theChar)</tt><tt>{</tt><tt>     if ( ! nextNode )</tt><tt>          nextNode = new Node(theChar);</tt><tt>     else</tt><tt>          nextNode-&gt;Insert(theChar);</tt><tt>}</tt></pre><p>This creates code that is a bit more difficult to maintain. As an object-oriented   designer, I notice that I'm asking each node to be capable of being the head   node in the list, an internal node in the list, or the tail of the list. There   is nothing special about these positions--they are just nodes. </p><p>Because any node can be internal or the tail, it can't know whether it has   a next node, so it must test. If a node knew that it was an internal node, it   would always know that there was a next node ("If I'm not the tail, there must   be at least one after me"). If it knew that it was the tail, it would know there   was no next node (such is the meaning of being the tail node; objects live a   very existential existence, and they often major in epistemology). <a name="_Toc450556045"></a></p><h2> <a name="Heading2">Specialization</a></h2><p>This leads me to a redesign. In it, I have three types of nodes: One is the   head node, one is the tail, and the third is the internal node. </p><p>You've already seen that the <tt>LinkedList</tt> class you created can mark   the head node position, and that this class has special responsibilities such   as supporting an offset operator. Let's break out the <tt>InternalNode</tt>   from the <tt>TailNode</tt>. </p><p>When we say that the <tt>LinkedList</tt>, <tt>InternalNode,</tt> and <tt>TailNode</tt>   are all nodes, this tells us that we are thinking about a specialization/generalization   relationship. The <tt>LinkedList</tt>, <tt>InternalNode</tt>, and <tt>TailNode</tt>   are specializations of <tt>Node</tt>. The <tt>LinkedList</tt> has the special   requirement that it act as an interface to the list, which leads me to the design   shown in Figure 8.1</p><p><b>Figure 8.1</b><tt><b> </b></tt><i>Node Specialization.</i></p><p>In this design, shown here in UML notation, we indicate that the <tt>LinkedList</tt>,   <tt>InternalNode</tt>, and <tt>TailNode</tt> are all kinds of <tt>Node</tt>.   This brings us back to the conversation about specialization/generalization   in Chapter 1, "Introduction."</p><p>The specialization relationship establishes an <i>is-a</i> relationship: That   is, a <tt>TailNode</tt> <i>is-a</i> <tt>Node</tt>. Furthermore, the specialization   relationship indicates that <tt>TailNode</tt> adds to the definition of <tt>Node</tt>.   It says that this is a special kind of <tt>Node</tt>--one that marks the end   of a list.</p><p>Similarly, <tt>InternalNode</tt> specializes <tt>Node</tt> to mean a node that   manages associated data and that, by implication, does not mark the tail of   the list.</p><p>Finally, <tt>LinkedList</tt> is-a node, a very special node that marks the   head of the list and that provides an interface to users of the list. We could   have called this the <i>head node</i>, but we want to focus on the user's perception:   To the user, the head node <i>is</i> the linked list. Thus, we bridge the gap   between the architect's view (in which this is the head node in a linked list)   and the user's view (in which this <i>is</i> the linked list) by having it inherit   from <tt>Node</tt> but calling it <tt>LinkedList</tt>.</p><blockquote>  <hr>  <p><strong>NOTE: </strong> The specialization/generalization relationship is     reciprocal. Because <tt>LinkedList</tt>, <tt>TailNode</tt>, and <tt>InternalNode</tt>     specialize <tt>Node</tt>, <tt>Node</tt> in turn becomes a generalization of     the commonality between all three of these classes. Programmers talk about     <i>factoring out</i> common features from the derived classes up into the     base classes. Thus, you can factor out all the common responsibilities of     the three classes up into <tt>Node</tt>. <a name="_Toc447529656"></a></p>  <hr></blockquote><h3> <a name="Heading3">Benefits from Specialization</a></h3><p>The first benefit you receive from this design is that <tt>Node</tt> can serve   to hold the common characteristics of <tt>LinkedList</tt>, <tt>InternalNode</tt>,   and <tt>TailNode</tt>. The things they have in common in this design are that   any <tt>Node</tt> can exist in a list, that you can tell any <tt>Node</tt> to   insert a new data object, and that you can tell a <tt>Node</tt> to display itself.</p><p>In addition, by specializing <tt>Node</tt>, this design of <tt>TailNode</tt>   maintains the <tt>Node</tt> features but adds the special capability to mark   the end of the list. This specialization is manifest in the differences in how   <tt>TailNode</tt> responds to a request to <tt>Insert</tt> data, which it handles   differently than, for example, an <tt>InternalNod <a name="_Toc447529657"></a>e</tt>   does.</p><h3> <a name="Heading4">Polymorphism </a></h3><p>The need to handle <tt>Insert()</tt> in a special way might be a good reason   to create a new class, but you can imagine that it would make your code much   more complicated. Each Node would have to know what it pointed to: If it pointed   to an <tt>InternalNode</tt>, it would call <tt>InternalNodeInsert</tt>, and   if it pointed to a <tt>TailNode</tt>, it would call <tt>TailNodeInsert</tt>.   What a bother. </p><p>We want to say, "I have a Node, I don't know what kind. It might be an <tt>InternalNode</tt>   or it might be a <tt>TailNode</tt>. When I call <tt>Insert()</tt>, I want my   <tt>Node</tt> to act one way if it is an <tt>InternalNode</tt>, and in a different   way if it is a <tt>TailNode</tt>."</p><p>This is called polymorphism: <i>poly</i> means <i>many</i> and <i>morph</i>   means <i>form</i>. We want the Node to take on many forms. C++ supports polymorphism,   which means that the client can call <tt>Insert</tt> on the <tt>Node</tt> and   the compiler will take care of making the right thing happen. In this case,   the right thing means that if the Node is really an <tt>InternalNode</tt>, <tt>InternalNode::Insert()</tt>   will be called; if the Node is really a <tt>TailNode</tt>, <tt>TailNode::Insert()</tt>   will be called instead. <a name="_Toc447529658"></a></p><h3> <a name="Heading5">Abstract Data Types</a></h3><p>You want to create <tt>InternalNode</tt> objects to hold your data, and you   want to create a single <tt>TailNode</tt> object and a single <tt>LinkedList</tt>   object. You will never instantiate a <tt>Node</tt> object, though. The <tt>Node</tt>   object exists only as an abstraction so that you can say "I will call the next   node," and not worry about which kind of node it is. The <tt>Node</tt> class   is called an <i>abstract data type (ADT)</i> because it exists only to provide   an abstraction for the classes that inherit from it.</p><p>The classes that are derived from your ADT (in this case, <tt>LinkedList</tt>,   <tt>InternalNode</tt>, and <tt>TailNode</tt>) can be <i>concrete,</i> and thus   can have objects instantiated. Alternatively, you can derive ADTs from other   ADTs. Ultimately, however, you must derive a concrete class so that you can   create objects.</p><blockquote>  <hr>  <p><strong> </strong> <b>Abstract Data Type</b>--A class that provides a common     interface to a number of classes that derive from it. You can never instantiate     an Abstract Data Type.</p>  <p> <b>concrete class</b>--A class that is not abstract and that can therefore     be instantiated. <a name="_Toc447529659"></a></p>  <hr></blockquote><h3> <a name="Heading6">How This Is Implemented in C++</a></h3><p>Until now, we've not discussed a word about how all this is implemented in   C++. That is because we have rightly been focused on design, not implementation. </p><p>The <i>design </i>calls for all three of these classes to specialize <tt>Node</tt>.   You <i>implement</i> that design concept of specialization by using inheritance.   Thus, you will have <tt>LinkedList</tt>, <tt>TailNode</tt>, and <tt>InternalNode</tt>   inherit from <tt>Node</tt>. <a name="_Toc447529660"></a></p><h3> <a name="Heading7">The Syntax of Inheritance</a></h3><p>When you declare a class, you can indicate the class from which it derives   by writing a colon after the class name, the type of derivation (public or otherwise),   and the class from which it derives. For now, focus only on public inheritance   because that is what implements the design concept of specialization/generalization.</p><p>Thus, to indicate that <tt>InternalNode</tt> is a specialization of <tt>Node</tt>,   or that <tt>InternalNode</tt> <i>derives</i> from <tt>Node</tt>, you write</p><pre><tt>class InternalNode : public Node</tt></pre><blockquote>  <hr>  <p><strong>NOTE: </strong> When one class specializes another, we say that the     specialized class is <i>derived</i> from the more general class, and that     the more general class is the <i>base class</i>.</p>  <hr></blockquote><p>The class from which you derive must have been declared already, or you receive   a compiler error. <a name="_Toc447529661"></a><a name="_Toc450556046"></a></p><h2> <a name="Heading8">Overriding Functions</a></h2><p>A <tt>LinkedList</tt> object has access to all the member functions in class   <tt>Node</tt>, as well as to any member functions the declaration of the <tt>LinkedList</tt>   class might add. It can also <i>override</i> a base class function. Overriding   a function means changing the implementation of a base class function in a derived   class. When you instantiate an object of the derived class and call an overridden   method, the right thing happens.</p><blockquote>  <hr>  <p><strong>NOTE: </strong> When a derived class creates a function with the     same return type and signature as a member function in the base class, but     with a new implementation, it is said to <i>override</i> that method.</p>  <hr></blockquote><p>This is very handy because it allows an <tt>InternalNode</tt> to specialize   how it handles some methods (such as <tt>Insert()</tt>) and simply inherit the   implementation of other methods.</p><p>When you override a function, its signature must be identical to the signature   of the function in the base class. The signature is the function prototype,   other than the return type: the name, the parameter list, and the keyword <tt>const</tt>   (if it is used). <a name="_Toc447529662"></a><a name="_Toc450556047"></a></p><h2> <a name="Heading9">Virtual Methods</a></h2><p>I have emphasized the fact that an <tt>InternalNode</tt> object is-a <tt>Node</tt>   object. So far that has meant only that the <tt>InternalNode</tt> object has   inherited the attributes (data) and capabilities (methods) of its base class.   In C++, however, the is-a relationship runs deeper than that.</p><p>C++ extends its polymorphism, allowing pointers to base classes to be assigned   to derived class objects. Thus, you can write</p><pre><tt>Node * pNode = new InternalNode;</tt></pre><p>This creates a new <tt>InternalNode</tt> object on the heap and returns a pointer   to that object, which it assigns to a pointer to <tt>Node</tt>. This is fine   because an <tt>InternalNode</tt> is-a <tt>Node</tt>.</p><p>In fact, this is the key to polymorphism. You can create all kinds of Windows--they   can each have a <tt>draw()</tt> method that does something different (the list   box draws a rectangle, the radio button draws a circle). You can create a pointer   to a Window without regard to what type of Window you have, and when you call</p><pre><tt>pWindow-&gt;Draw();</tt></pre><p>the Window is drawn properly.</p><p>Similarly, you can have a pointer to any kind of <tt>Node</tt>, a <tt>LinkedList</tt>,   an <tt>InternalNode,</tt> or a <tt>TailNode</tt>, and you can call <tt>Insert()</tt>   on that node without regard to what kind of <tt>Node</tt> it is. The right thing   will happen. </p><p>Here's how it works: You use the pointer to invoke a method on <tt>Node</tt>,   for example <tt>Insert()</tt>. If the pointer is really pointing to a <tt>TailNode</tt>,   and if <tt>TailNode</tt> has overridden <tt>Insert()</tt>, the overridden version   of <tt>Insert</tt> is called. If <tt>TailNode</tt> does not override <tt>Insert()</tt>,   it inherits this method from its base class, <tt>Node</tt>, and <tt>Node::Insert()</tt>   is called.</p><p>This is accomplished through the magic of virtual functions. </p><blockquote>  <hr>  <p><strong>NOTE: </strong> C++ programmers use the terms <i>method</i> and <i>function</i>     interchangeably. This confusion comes from the fact that C++ has two parents:     the object-oriented languages such as SmallTalk, which use the term <i>method</i>,     and C, which uses the term <i>function</i>. <a name="_Toc447529663"></a></p>  <hr></blockquote><h3> <a name="Heading10">How Virtual Functions Work</a></h3><p>When a derived object, such as an <tt>InternalNode</tt> object, is created, 

⌨️ 快捷键说明

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