📄 ch08.htm
字号:
<tt>106: cin >> guess;</tt><tt>107: </tt><tt>108: if ( strlen(guess) != howManyPositions )</tt><tt>109: {</tt><tt>110: cout << "\n ** Please enter exactly ";</tt><tt>111: cout << howManyPositions << " letters. **\n";</tt><tt>112: continue;</tt><tt>113: }</tt><tt>114: </tt><tt>115: </tt><tt>116: round++;</tt><tt>117: </tt><tt>118: cout << "\nYour guess: " << guess << endl;</tt><tt>119: </tt><tt>120: Score(guess,correct,position);</tt><tt>121: cout << "\t\t" << correct << " correct, ";</tt><tt>122: cout << position << " in position." << endl;</tt><tt>123: }</tt><tt>124: </tt><tt>125: cout << "\n\nCongratulations! It took you ";</tt><tt>126: </tt><tt>127: if ( round <= 6 )</tt><tt>128: cout << "only ";</tt><tt>129: </tt><tt>130: if ( round-1 == 1 )</tt><tt>131: cout << "one round!" << endl;</tt><tt>132: else</tt><tt>133: cout << round-1 << " rounds." << endl;</tt><tt>134: </tt><tt>135: }</tt><tt>136: </tt><tt>137: </tt><tt>138: void Game::Score(</tt><tt>139: const char * thisGuess, </tt><tt>140: int & correct, </tt><tt>141: int & position</tt><tt>142: )</tt><tt>143: {</tt><tt>144: correct = 0;</tt><tt>145: position = 0;</tt><tt>146: </tt><tt>147: </tt><tt>148: for ( int i = 0; i < howManyLetters; i++)</tt><tt>149: {</tt><tt>150: int howManyInGuess = HowMany(thisGuess, alpha[i]);</tt><tt>151: int howManyInAnswer = solution.HowMany(alpha[i]);</tt><tt>152: correct += howManyInGuess < howManyInAnswer ? </tt><tt>153: howManyInGuess : howManyInAnswer;</tt><tt>154: }</tt><tt>155: </tt><tt>156: for ( i = 0; i < howManyPositions; i++)</tt><tt>157: {</tt><tt>158: if ( thisGuess[i] == solution[i] )</tt><tt>159: position++;</tt><tt>160: }</tt><tt>161: </tt><tt>162: ASSERT ( position <= correct );</tt><tt>163: </tt><tt>164: }</tt></pre><h4> Listing 8.6 Decryptix! Driver Program</h4><pre><tt>0: #include "definedValues.h"</tt><tt>1: #include "game.h"</tt><tt>2: </tt><tt>3: int main()</tt><tt>4: {</tt><tt>5: </tt><tt>6: cout << "Decryptix. Copyright 1999 Liberty Associates,";</tt><tt>7: cout << " Inc. Version 0.3\n\n" << endl;</tt><tt>8: bool playAgain = true;</tt><tt>9: </tt><tt>10: while ( playAgain )</tt><tt>11: {</tt><tt>12: char choice = ' ';</tt><tt>13: Game theGame;</tt><tt>14: theGame.Play();</tt><tt>15: </tt><tt>16: cout << "\nThe answer: ";</tt><tt>17: theGame.GetSolution().Display();</tt><tt>18: cout << "\n\n" << endl;</tt><tt>19: </tt><tt>20: while ( choice != 'y' && choice != 'n' )</tt><tt>21: {</tt><tt>22: cout << "\nPlay again (y/n): ";</tt><tt>23: cin >> choice;</tt><tt>24: }</tt><tt>25: </tt><tt>26: playAgain = choice == 'y' ? true : false; </tt><tt>27: }</tt><tt>28: </tt><tt>29: return 0;</tt><tt>30: }</tt></pre><blockquote> <hr> <p><strong>NOTE: </strong> The <tt>Game</tt> class declaration is unchanged from the previous version, and is reproduced here only as a convenience. </p> <hr></blockquote><p>Let's start the analysis with the construction of the solution member variable of <tt>Game</tt>. Put a break point on line 58 of Listing 8.2. When the break point is hit, the first thing you do is check the call stack to see when in the execution of the program this constructor was called:</p><pre><tt>LinkedList::LinkedList() line 62</tt><tt>Game::Game() line 10 + 58 bytes</tt><tt>main() line 14 + 8 bytes</tt></pre><p>As you can see, <tt>main()</tt> called the <tt>Game</tt> constructor, which in turn called the <tt>LinkedList</tt> constructor. Line 14 of <tt>main()</tt> looks like this:</p><pre><tt> Game theGame;</tt></pre><p>It's just as you expected--the construction of a <tt>Game</tt> object. Line 10 of <tt>Game</tt> is the opening brace, shown in the code as line 9 of Listing 8.4. Because the <tt>LinkedList</tt> member variable (<tt>solution</tt>) was not initialized, the compiler calls its constructor just before entering the body of <tt>Game</tt>'s constructor.</p><p>Returning to Listing 8.2, line 58, note that the <tt>LinkedList</tt> initializes its <tt>duplicates</tt> member variable to <tt>true</tt> (line 59); then, on line 61, it creates a new <tt>TailNode</tt> and sets its own <tt>nextNode</tt> to point to the <tt>Tail</tt>. This creates an empty linked list, as shown in Figure 8.5.</p><p><b>Figure 8.5</b><tt><b> </b></tt><i>An empty linked list.</i></p><p><tt>LinkedList</tt> is thus automatically initialized to a first <tt>Node</tt> (<tt>LinkedList</tt>) and a last <tt>Node</tt> (<tt>TailNode</tt>); neither of them contains any data, but together they create the structure of the list.</p><p>If you continue stepping through the code, you find yourself in the body of the <tt>Game</tt> constructor (line 11 of Listing 8.4). Set a break point on line 66 and run to that break point so that you skip examining the initial user interface code that has not changed from previous chapters.</p><p>When you are prompted, choose five letters in five positions. The break point is hit, and we generate a seed for the random number generator, based on the time (as discussed in previous chapters). On line 70, we generate a random number and use that as an offset into the <tt>alpha</tt> array to generate our first character. By line 72 we have that first character, which in my case is <tt>'d'</tt>. </p><p>Stepping into the <tt>Add</tt> method causes us to jump to line 92 of Listing 8.2. On line 94, <tt>duplicates</tt> is tested and fails (we're not allowing dupes); therefore, the call to <tt>HowMany</tt> is made, passing in the parameter.</p><p>Stepping in here brings us to line 74. The <tt>LinkedList</tt> implementation of this is to return the value that is generated by calling <tt>HowMany</tt> on whatever the <tt>LinkedList</tt> points to. Step in, and you'll find yourself in the <tt>HowMany()</tt> method of <tt>TailNode</tt>. This makes sense; right now, <tt>LinkedList</tt> points to <tt>TailNode</tt>.</p><p>Because <tt>TailNode</tt> holds no data, it returns <tt>0</tt> regardless of what character it is given. This is returned to <tt>LinkedList::HowMany()</tt>, which in turn returns it to <tt>LinkedList::Add()</tt> on line 94. Because this satisfies the second condition in the <tt>OR</tt> statement, enter the body of the <tt>if</tt> statement on line 96. </p><p>Stepping into the <tt>Call</tt> to <tt>Insert</tt> jumps to line 80, the implementation of <tt>LinkedList::Insert()</tt>. <tt>LinkedList</tt>'s strategy is to pass this request on to whatever it points to. Stepping in brings us to line 47, the implementation of <tt>TailNode::Insert()</tt>.</p><p><tt>TailNode</tt> always inserts a node when it is asked to. It knows that it is the tail, so it doesn't have to check--it can just make the insertion. This is the critical difference from the previous version. You'll remember that in Chapter 6, "Using Linked Lists," <tt>Node</tt> responded to <tt>Insert</tt> as follows:</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->Insert(theChar);</tt><tt>}</tt></pre><p>That is, it was necessary to see whether there were any more <tt>Nodes</tt> in the list. If not (if the current <tt>Node</tt> was the <tt>Tail</tt>), a new <tt>Node</tt> could be inserted. On the other hand, if the current <tt>Node</tt> was not the <tt>Tail</tt>, and therefore was an <tt>InternalNode</tt>, the <tt>Insert</tt> request would be passed down the list.</p><p>The new design obviates the need for the test: The <tt>TailNode</tt> knows that it is the tail, and it can just make the insertion. This simple division of responsibility is, in a small way, the very heart of object-oriented software development.</p><p>Let's examine the implementation in some detail. Line 47 returns the address of the new <tt>InternalNode</tt> that is created by passing in the character that is received as a parameter and the <tt>this</tt> pointer of the <tt>TailNode</tt>.</p><p>This jumps to the constructor for <tt>InternalNode</tt>, shown on line 2. Here you see the creation of the <tt>InternalNode</tt>. The character is inserted into the <tt>InternalNode</tt>'s <tt>myChar</tt> member variable, and the <tt>this</tt> pointer from the <tt>TailNode</tt> is stored in the <tt>nextNode</tt> pointer of <tt>InternalNode</tt>. </p><p>The address of this <tt>InternalNode</tt> is then passed back to the caller of <tt>TailNode::Insert()</tt>, and in this case is assigned to the <tt>nextNode</tt> member variable of <tt>LinkedList</tt> (as shown on line 81). Finally, this address is returned by <tt>LinkedList::Insert</tt>, but the calling function--<tt>LinkedList::Add()</tt> on line 96--makes no use of it, and it is thrown on the floor. On line 97, we return <tt>true</tt> to the calling function on line 72 of Listing 8.4</p><p>We have now added a first letter to the linked list, and it worked great. It takes a lot longer to explain the process than to perform it. Next, let's track the second letter, now that you have an <tt>InternalNode</tt> in the linked list. <a name="_Toc447529666"></a></p><h3> <a name="Heading13">Adding a Second Letter</a></h3><p>Return to line 92 of Listing 8.2. Once again, this steps you into <tt>LinkedList::HowMany()</tt> (line 74), this time passing in <tt>'b'</tt>.</p><p>This time, <tt>LinkedList</tt>'s <tt>nextNode</tt> points to an <tt>InternalNode</tt> (the one holding <tt>'d'</tt>), so we now jump to line 17. Here a local variable <tt>myCount</tt> is initialized to zero. The <tt>InternalNode</tt>'s member variable <tt>myCount</tt> (<tt>'d'</tt>) is compared to the parameter <tt>theChar</tt> (<tt>'b'</tt>). Because they are not the same, <tt>myCount</tt> remains zero. </p><p>We now invoke <tt>HowMany()</tt> on the node that is pointed to by this <tt>InternalNode</tt>'s <tt>nextNode</tt> pointer. Right now, the pointer points to <tt>TailNode</tt>, which we examined previously; it simply returns zero. That zero is added to <tt>myCount</tt> (also zero) for a total of zero, which is the value that is returned to <tt>LinkedList::Add()</tt>.</p><p>This causes the second half of the <tt>OR</tt> statement on line 94 to return <tt>true</tt> (<tt>HowMany('b')</tt> equals zero), so the body of the <tt>if</tt> statement executes. This causes the call on <tt>Insert()</tt> to execute, with a jump to line 80, which is the implementation of <tt>LinkedList::Insert()</tt>. Again, <tt>LinkedList</tt>'s strategy is to pass this request on to whatever it points to, which in this case is <tt>InternalNode</tt>'s <tt>Insert()</tt> method (as shown on line 27). <tt>InternalNode</tt>'s strategy is to call <tt>Insert()</tt> on the object to which its <tt>nextNode</tt> pointer points (in this case <tt>TailNode</tt>), and then to set its <tt>nextNode</tt> pointer to whatever value is returned. </p><p>Because the <tt>nextNode</tt> is the <tt>TailNode</tt>, <tt>InternalNode::Insert</tt> is now called. As you saw just a moment ago, it creates a new <tt>InternalNode</tt> for <tt>'b'</tt> and tells that new node to point to the tail. It then returns the address of the new node, which is now assigned to the <tt>nextNode</tt> pointer of the node that holds <tt>'d'</tt>. Thus, <tt>'b'</tt> is appended to the list, as shown in Figure 8.6.</p><p><b>Figure 8.6</b><tt><b> <a name="_Toc447529667"></a></b></tt></p><h3> <a name="Heading14">Appending 'b'.Examining operator[]</a></h3><p>Let's take a look at <tt>Game::Score</tt>, beginning on line 138 of Listing 8.4. You've examined the fundamental logic in detail in previous chapters. (We're particularly interested in the letter by letter comparisons.) You've seen how <tt>LinkedList::HowMany()</tt> works; now take a look at the offset operator as it is used on line 158.</p><p>Stepping in to this code steps into <tt>Linkedlist::Operator[]</tt> on line 87 of Listing 8.2. </p><blockquote> <hr> <p><strong>NOTE: </strong> Along the way, I've added test code at line 77 to print out the answer so that I can examine the behavior of the system as I test it. You want to remove both lines 76 and 77 before releasing this code. </p> <hr></blockquote><p>Not surprisingly, all <tt>LinkedList</tt> does here is invoke this same operator on the node to which it points. Stepping in from line 87 brings you to line 31, <tt>InternalNode::operator[]</tt>. Take a look at your auto member variables, and you'll find that <tt>myChar</tt> is <tt>'d'</tt>, just as you might expect. The offset that was passed in is now tested. If it is <tt>0</tt>, the call was for the first letter in the list. You will be at the first letter in the list, and you can return <tt>myChar</tt>, which is the case now. The letter <tt>'d'</tt> is returned to <tt>LinkedList</tt>; <tt>LinkedList</tt> returns it to the calling method, <tt>score()</tt>, which--on line 158 of Listing 8.4--is compared with the value of the first character in the guess.</p><p>This is repeated, but with <tt>i</tt> set to <tt>1</tt>. A call to <tt>solution[1]</tt> is invoked, bringing us back into <tt>LinkedList::operator[]</tt> on line 87 of Listing 8.2. </p><p>Stepping in from line 87 brings you back to <tt>InternalNode::operator[]</tt> on line 31. Take a look at your auto member variables, and you'll find that <tt>myChar</tt> is again <tt>'d'</tt>--you're back at the first <tt>InternalNode</tt> in the list. The offset that was passed in (<tt>1</tt>) is now tested. Because it is not zero, the <tt>if</tt> statement on line 33 fails and the body of the <tt>else</tt> statement on line 36 executes:</p><pre><tt>36: return (*nextNode)[--offSet];</tt></pre><p>This invokes the offset operator on the next <tt>Node</tt> in the list, passing in the decremented <tt>offSet</tt> value. Stepping in appears to bring us back to the top of the same method, but check your variables--<tt>myChar</tt> is now <tt>b</tt>, and <tt>offset</tt> is now zero. Perfect: You'll return the second letter in <a name="_Toc447529668"></a>the list, exactly as you wanted.</p><p>The linked list works as you want, and by using inheritance, you've delegated responsibility for monitoring the head and tail of the list to specialized nodes. This simplifies the code and makes it easier to maintain.</p><p>The problem with this linked list, however, is that it can only be used by nodes that hold single characters. What if you have other data that you want to insert into your linked list? Must you really rewrite the linked list each time you change the kind of object that is contained? The next chapter, "Implementing Templates," takes a look at modifying your linked list so that it can handle any kind of data. </p><CENTER><P><HR> <A HREF="../index.htm"><IMG SRC="../button/contents.gif" WIDTH="128"HEIGHT="28" ALIGN="BOTTOM" ALT="Contents" BORDER="0"></A> <BR> <BR><p></P><P>© <A HREF="../copy.htm">Copyright 1999</A>, Macmillan Computer Publishing. Allrights reserved.</p></CENTER></BODY></HTML>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -