ch04.htm
来自「C++ From Scratch: An Object-Oriented App」· HTM 代码 · 共 1,077 行 · 第 1/4 页
HTM
1,077 行
</blockquote><p>It is through these member methods that an object of a class achieves its behavior. <a name="_Toc441891052"></a><a name="_Toc444149702"></a></p><h3> <a name="Heading11">The Size of Objects</a></h3><p>The size of an object is the sum of the sizes of the member variables that are declared for its class. Thus, if an <tt>int</tt> is 4 bytes and your class declares three integer member variables, each object is 12 bytes. Functions have no size. <a name="_Toc444149703"></a><a name="_Toc450554045"></a></p><h2> <a name="Heading12">Files</a></h2><p>You create a class in two steps: First, the interface to the class is declared in a <i>header file</i>; second, the member methods are created in a <i>source code file</i>. </p><blockquote> <hr> <p><strong>NOTE: </strong> <b>Header file</b>--A text file that contains the class declaration. Traditionally named with the .h extension</p> <p> <b>Source file</b>--A text file that contains the source code for the member methods of a class. Traiditionally named with the .cpp extension</p> <hr></blockquote><p>The header file typically has an extension of .h or .hpp, and the source code file has the extension .cpp. So for your <tt>Game</tt> class, you can expect to find the declaration in the file Game.h and the implementation of the class methods in Game.cpp. <a name="_Toc444149704"></a><a name="_Toc450554046"></a></p><h2> <a name="Heading13">Constructors</a></h2><p>It is not uncommon for a class to require a bit of setting up before it can be used. In fact, an object of that class might not be considered valid if it hasn't been set up properly. C++ provides a special method to set up and initialize each object, called a <i>constructor, </i>as shown at line 3. </p><p>In this case, you want the constructor to initialize each of the member variables. For some member variables, you'll <i>hard wire</i> a reasonable value; for example, you'll keep track of what round of play you are on, and of course, you'll start with round 1.</p><blockquote> <hr> <p><strong>NOTE: </strong> <i>Hard wire</i> is a programming term that means that the value is written into the code and doesn't change each time you run the program. </p> <hr></blockquote><p>For other member variables, you must ask the user to choose an appropriate starting value. For example, you'll ask the user to tell you whether duplicates are allowed, how many letters are to be used, and how many positions are to appear in the secret code.</p><p>A constructor (line 3) has the same name as the class itself, and never has a return value.</p><blockquote> <hr> <p><strong>NOTE: </strong> The absence of a return value does not, in this case, mean that it returns <tt>void</tt>. Constructors are special: They have no return value. There are only two types of methods for which this is true--constructors and destructors. <a name="_Toc444149705"></a></p> <hr></blockquote><h3> <a name="Heading14">Destructors</a></h3><p>The job of the destructor (line 4) is to tear down the object. This idea will make more sense after we talk about allocating memory or other resources. For now, the destructor won't do much, but as a matter of form, if I create a constructor, I always create a destructor. <a name="_Toc444149706"></a><a name="_Toc450554047"></a></p><h2> <a name="Heading15">Implementing the Methods</a></h2><p>The header file provides the interface. Each of the methods is named, but the actual implementation is not in this file--it is in the implementation file (See Listing 4.2).</p><h4> Listing 4.2 Game.cpp</h4><pre><tt>0: #include "Game.h"</tt><tt>1: #include <iostream.h></tt><tt>2: </tt><tt>3: </tt><tt>4: Game::Game():</tt><tt>5: round(1),</tt><tt>6: howManyPositions(0),</tt><tt>7: howManyLetters(0),</tt><tt>8: duplicatesAllowed(false)</tt><tt>9: {</tt><tt>10: enum BoundedValues </tt><tt>11: { </tt><tt>12: minPos = 2, </tt><tt>13: maxPos = 10, </tt><tt>14: minLetters = 2, </tt><tt>15: maxLetters = 26 </tt><tt>16: };</tt><tt>17: bool valid = false;</tt><tt>18: while ( ! valid )</tt><tt>19: {</tt><tt>20: while ( howManyLetters < minLetters </tt><tt>21: || howManyLetters > maxLetters )</tt><tt>22: {</tt><tt>23: cout << "How many letters? (";</tt><tt>24: cout << minLetters << "-" << maxLetters << "): ";</tt><tt>25: cin >> howManyLetters;</tt><tt>26: if ( howManyLetters < minLetters </tt><tt>27: || howManyLetters > maxLetters )</tt><tt>28: {</tt><tt>29: cout << "please enter a number between "; </tt><tt>30: cout << minLetters << " and " << maxLetters << endl;</tt><tt>31: }</tt><tt>32: }</tt><tt>33: </tt><tt>34: while ( howManyPositions < minPos </tt><tt>35: || howManyPositions > maxPos )</tt><tt>36: {</tt><tt>37: cout << "How many positions? (";</tt><tt>38: cout << minPos << "-" << maxPos << "): ";</tt><tt>39: cin >> howManyPositions;</tt><tt>40: if ( howManyPositions < minPos </tt><tt>41: || howManyPositions > maxPos )</tt><tt>42: {</tt><tt>43: cout << "please enter a number between ";</tt><tt>44: cout << minPos <<" and " << maxPos << endl;</tt><tt>45: }</tt><tt>46: }</tt><tt>47: </tt><tt>48: char choice = ' ';</tt><tt>49: while ( choice != 'y' && choice != 'n' )</tt><tt>50: {</tt><tt>51: cout << "Allow duplicates (y/n)? ";</tt><tt>52: cin >> choice;</tt><tt>53: }</tt><tt>54: </tt><tt>55: duplicatesAllowed = choice == 'y' ? true : false;</tt><tt>56: </tt><tt>57: if ( ! duplicatesAllowed && </tt><tt>58: howManyPositions > howManyLetters )</tt><tt>59: {</tt><tt>60: cout << "I can't put " << howManyLetters;</tt><tt>61: cout << " letters in " << howManyPositions;</tt><tt>62: cout << " positions without duplicates! Please try again.\n";</tt><tt>63: howManyLetters = 0;</tt><tt>64: howManyPositions = 0;</tt><tt>65: }</tt><tt>66: else</tt><tt>67: valid = true;</tt><tt>68: }</tt><tt>69: </tt><tt>70: </tt><tt>71: }</tt><tt>72: </tt><tt>73: Game::~Game()</tt><tt>74: {</tt><tt>75: </tt><tt>76: }</tt><tt>77: </tt><tt>78: void Game::Play()</tt><tt>79: {</tt><tt>80: </tt><tt>81: }</tt></pre><p>Listing 4.3 provides a short driver program that does nothing but instantiate an object of type <tt>Game</tt>.</p><h4> Listing 4.3 Decryptix.cpp</h4><pre><tt>0: #include <iostream ></tt><tt>1: #include "Game.h"</tt><tt>2: </tt><tt>3: int main()</tt><tt>4: {</tt><tt>5: Game theGame;</tt><tt>6: return 0;</tt><tt>8: }<a name="_Toc444149707"></a><a name="_Toc450554048"></a></tt></pre><h2> <a name="Heading16">Including the Header</a></h2><p>The compiler can't know what a <tt>Game</tt> is without the definition, which is in the header file. To tell the compiler what a <tt>Game</tt> object is, the first thing you do in the implementation file is to <tt>#include</tt> the file with the definition of the <tt>Game</tt> class, in this case Game.h (as shown on line 1 of Listing 4.2).</p><blockquote> <hr> <p><strong>NOTE: </strong> It is desirable to minimize the number of header files that are included in other header files. Having many <tt>include</tt> statements within a header file can risk the creation of circular references (a includes b, which includes c, which includes a) that won't compile. This can also introduce <i>order dependence</i>, which means that the proper execution of your code depends on files being added in the "correct order." This makes for code that is difficult to maintain.</p> <p> There is no limit to the number of header files you might want to include in implementation files, but keep the includes in your header file to a minimum. <a name="_Toc444149708"></a><a name="_Toc450554049"></a></p> <hr></blockquote><h2> <a name="Heading17">Implementing the Constructor</a></h2><p>A member function definition begins with the name of the class, followed by two colons (the scoping operator), the name of the function, and its parameters. On line 4 in Listing 4.2, you can see the implementation of the constructor. </p><blockquote> <hr> <p><strong> </strong> <b>scope operator</b>--The pair of colons between the class name and the method</p> <p> <b>identifier</b>--Any named thing: object, method, class, variable, and so on </p> <hr></blockquote><p>Like all methods, the constructor begins with an open brace (<tt>{</tt>) and ends with a closing brace (<tt>}</tt>). The body of the constructor lies between the braces. <a name="_Toc444149709"></a><a name="_Toc450554050"></a></p><h2> <a name="Heading18">Initialization</a></h2><p>In the exploration of variables, I talked about the difference between assignment and initialization. Member variables can be initialized as well. In fact, the constructor actually executes in two steps:</p><ul> <li> <p> Initialization</p> </li> <p></p> <li> <p> Construction</p> </li></ul><p></p><p>Construction is accomplished in the body of the constructor. Initialization is accomplished through the syntax that is shown: After the closing parentheses on the constructor, add a colon. For each member variable you want to initialize, write the variable name, followed by the value to which you want to initialize it (enclosed in parentheses). Note also that you can initialize multiple members by separating them with commas. There must be no comma after the last initialized value.</p><p>Thus, on line 5 in Listing 4.3, you see <tt>round</tt> initialized to the value <tt>1</tt>, <tt>howManyPositions</tt> to the value <tt>0</tt>, <tt>howManyLetters</tt> to the value <tt>0</tt>, and <tt>duplicatesAllowed</tt> to the value <tt>false</tt>.</p><blockquote> <hr> <p><strong>NOTE: </strong> The new line I've placed between each initialized value is only for the convenience of the programmer. I can just as easily put them all on one line, separated by spaces:</p> <hr></blockquote><pre><tt>Game::Game(): </tt><tt>round(1), </tt><tt>howManyPositions(0), </tt><tt><tt>howManyLetters</tt>(0), </tt><tt><tt>duplicates</tt>Allowed(false)</tt><tt>{</tt></pre><p>All this initialization occurs before the body of the constructor runs, beginning on line 10 of Listing 4.2 <a name="WhereWasI"></a>.</p><blockquote> <hr> <p><strong>NOTE: </strong> We talk of methods or functions running, being executed, or being called, depending on context. These all mean the same thing: Program execution branches to the function, beginning at the first line and proceeding from there until it reaches a return statement.</p> <hr></blockquote><p>Within the body of the constructor, you see that an enumerated constant, <tt>BoundedValues</tt>, is created, and a <i>local</i> variable, <tt>valid</tt>, is created and initialized on line 17.</p><p>This local variable, <tt>valid</tt>, will exist only for the duration of the constructor. Because this value is needed only temporarily and is not part of the permanent state of the object (it is not an attribute of the class <tt>Game</tt>), do not make it a member variable.</p><p>Just as <tt>valid</tt> is a variable that is local to the constructor, the instance of <tt>Game</tt> that is created in <tt>main()</tt>is local to <tt>main()</tt> (Listing 4.3, line 5). Declare it like you declare any other variable--by declaring its type (<tt>Game</tt>), and then the name of the object itself (<tt>theGame</tt>). You can name the object anything you want, but it is best to name it something meaningful so that the code can be easily understood.</p><p>By defining this object, you bring it into existence, and that causes the constructor to be invoked automatically. </p><p>Normally, methods are called <i>explicitly</i>. The constructor, however, is called <i>implicitly</i> when the object is created, and the destructor is called implicitly when the object is destroyed. When a method is called implicitly, the call doesn't appear in your code: It is understood to be the result of another action. Thus, when you create an object, you implicitly call the constructor; when you delete an object, you implicitly call the destructor. Not only do you not have to call these methods explicitly, you are prohibited from doing so.</p><p>There are two ways to see this explicitly. One way is to add a temporary output line to the constructor and destructor (as shown in Listing 4.4), and to <tt>main()
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?