?? chapter13.html
字號:
<HTML><HEAD> <TITLE>Chapter 13</TITLE> <LINK REL="STYLESHEET" HREF="downey.css" tppabs="http://rocky.wellesley.edu/downey/ost/thinkCS/c++_html/downey.css"></HEAD><BODY><H2>Chapter 13</H2><H1>Objects of Vectors</H1><H3>13.1 Enumerated types</H3><P>In the previous chapter I talked about mappings between real-world values like rank and suit, and internal representations like integers and strings. Although we created a mapping between ranks and integers, and between suits andintegers, I pointed out that the mapping itself does not appear as part of theprogram.</P><P>Actually, C++ provides a feature called and <B>enumerated type</B> that makes it possible to (1) include a mapping as part of the program, and (2) define the set of values that make up the mapping. For example, here is the definition of the enumerated types <TT>Suit</TT> and <TT>Rank</TT>:</P><PRE>enum Suit { CLUBS, DIAMONDS, HEARTS, SPADES };enum Rank { ACE=1, TWO, THREE, FOUR, FIVE, SIX, SEVEN, EIGHT, NINE,TEN, JACK, QUEEN, KING };</PRE><P>By default, the first value in the enumerated type maps to 0, the second to 1, and so on. Within the <TT>Suit</TT> type, the value <TT>CLUBS</TT> is represented by the integer 0, <TT>DIAMONDS</TT> is represented by 1, etc.</P><P>The definition of <TT>Rank</TT> overrides the default mapping and specifies that <TT>ACE</TT> should be represented by the integer 1. The other values follow in the usual way.</P><P>Once we have defined these types, we can use them anywhere. For example, theinstance variables <TT>rank</TT> and <TT>suit</TT> are can be declared with type <TT>Rank</TT> and <TT>Suit</TT>:</P><PRE>struct Card{ Rank rank; Suit suit; Card (Suit s, Rank r);};</PRE><P>That the types of the parameters for the constructor have changed, too. Now,to create a card, we can use the values from the enumerated type as arguments:</P><PRE> Card card (DIAMONDS, JACK);</PRE><P>By convention, the values in enumerated types have names with all capital letters. This code is much clearer than the alternative using integers:</P><PRE> Card card (1, 11);</PRE><P>Because we know that the values in the enumerated types are represented as integers, we can use them as indices for a vector. Therefore the old <TT>print</TT> function will work without modification. We have to make some changes in <TT>buildDeck</TT>, though:</P><PRE> int index = 0; for (Suit suit = CLUBS; suit <= SPADES; suit = Suit(suit+1)) { for (Rank rank = ACE; rank <= KING; rank = Rank(rank+1)) { deck[index].suit = suit; deck[index].rank = rank; index++; } }</PRE><P>In some ways, using enumerated types makes this code more readable, but there is one complication. Strictly speaking, we are not allowed to do arithmetic with enumerated types, so <TT>suit++</TT> is not legal. On the otherhand, in the expression <TT>suit+1</TT>, C++ automatically converts the enumerated type to integer. Then we can take the result and typecast it back tothe enumerated type:</P><PRE> suit = Suit(suit+1); rank = Rank(rank+1);</PRE><P>Actually, there is a better way to do this---we can define the <TT>++</TT> operator for enumerated types---but that is beyond the scope of this book.</P><BR><BR><H3><TT>switch</TT> statement</H3><P>It's hard to mention enumerated types without mentioning <TT>switch</TT> statements, because they often go hand in hand. A <TT>switch</TT> statementis an alternative to a chained conditional that is syntactically prettier and often more efficient. It looks like this:</P><PRE> switch (symbol) { case '+': perform_addition (); break; case '*': perform_multiplication (); break; default: cout << "I only know how to perform addition and multiplication" << endl; break; }</PRE><P>This <TT>switch</TT> statement is equivalent to the following chainedconditional:</P><PRE> if (symbol == '+') { perform_addition (); } else if (symbol == '*') { perform_multiplication (); } else { cout << "I only know how to perform addition and multiplication" << endl; }</PRE><P>The <TT>break</TT> statements are necessary in each branch in a <TT>switch</TT> statement because otherwise the flow of execution ``falls through'' to the next case. Without the <TT>break</TT> statements, the symbol <TT>+</TT> would make the program perform addition, and then perform multiplication, and then print the error message. Occasionally this feature is useful, but most of the time it is a source of errors when people forget the <TT>break</TT> statements.</P><P><TT>switch</TT> statements work with integers, characters, and enumerated types. For example, to convert a <TT>Suit</TT> to the corresponding string, we could use something like:</P><PRE> switch (suit) { case CLUBS: return "Clubs"; case DIAMONDS: return "Diamonds"; case HEARTS: return "Hearts"; case SPADES: return "Spades"; default: return "Not a valid suit"; }</PRE><P>In this case we don't need <TT>break</TT> statements because the <TT>return</TT> statements cause the flow of execution to return to the caller instead of falling through to the next case.</P><P>In general it is good style to include a <TT>default</TT> case in every <TT>switch</TT> statement, to handle errors or unexpected values.</P><BR><BR><H3>13.3 Decks</H3><P>In the previous chapter, we worked with a vector of objects, but I also mentioned that it is possible to have an object that contains a vector as an instance variable. In this chapter I am going to create a new object, called a <TT>Deck</TT>, that contains a vector of <TT>Card</TT>s.</P><P>The structure definition looks like this</P><PRE>struct Deck { apvector<Card> cards; Deck (int n);};Deck::Deck (int size){ apvector<Card> temp (size); cards = temp;}</PRE><P>The name of the instance variable is <TT>cards</TT> to help distinguish the <TT>Deck</TT> object from the vector of <TT>Card</TT>s that it contains.</P><P>For now there is only one constructor. It creates a local variable named <TT>temp</TT>, which it initializes by invoking the constructor for the <TT>apvector</TT> class, passing the size as a parameter. Then it copies the vector from <TT>temp</TT> into the instance variable <TT>cards</TT>.</P><P>Now we can create a deck of cards like this:</P><PRE> Deck deck (52);</PRE><P>Here is a state diagram showing what a <TT>Deck</TT> object looks like:</P><P CLASS=1><IMG SRC="images/deckobject.png" tppabs="http://rocky.wellesley.edu/downey/ost/thinkCS/c++_html/images/deckobject.png"></P><P>The object named <TT>deck</TT> has a single instance variable named <TT>cards</TT>, which is a vector of <TT>Card</TT> objects. To access the cardsin a deck we have to compose the syntax for accessing an instance variable and the syntax for selecting an element from an array. For example, the expression <TT>deck.cards[i]</TT> is the ith card in the deck, and <TT>deck.cards[i].suit</TT> is its suit. The following loop</P><PRE> for (int i = 0; i<52; i++) { deck.cards[i].print(); }</PRE><P>demonstrates how to traverse the deck and output each card.</P><BR><BR><H3>13.4 Another constructor</H3><P>Now that we have a <TT>Deck</TT> object, it would be useful to initialize the cards in it. From the previous chapter we have a function called <TT>buildDeck</TT> that we could use (with a few adaptations), but it might be more natural to write a second <TT>Deck</TT> constructor.</P><PRE>Deck::Deck (){ apvector<Card> temp (52); cards = temp; int i = 0; for (Suit suit = CLUBS; suit <= SPADES; suit = Suit(suit+1)) { for (Rank rank = ACE; rank <= KING; rank = Rank(rank+1)) { cards[i].suit = suit; cards[i].rank = rank; i++; } }}</PRE><P>Notice how similar this function is to <TT>buildDeck</TT>, except that we had to change the syntax to make it a constructor. Now we can create a standard52-card deck with the simple declaration <TT>Deck deck;</TT></P><BR><BR><H3>13. 5 <TT>Deck</TT> member functions</H3>Now that we have a <TT>Deck</TT> object, it makes sense to put all the functions that pertain to <TT>Deck</TT>s in the <TT>Deck</TT> structure definition. Looking at the functions we have written so far, one obvious candidate is <TT>printDeck</TT> (Section~\ref{printdeck</TT>). Here's how it looks, rewritten as a <TT>Deck</TT> member function:</P><PRE>void Deck::print () const { for (int i = 0; i < cards.length(); i++) { cards[i].print (); }}</PRE><P>As usual, we can refer to the instance variables of the current object without using dot notation.</P><P>For some of the other functions, it is not obvious whether they should be member functions of <TT>Card</TT>, member functions of <TT>Deck</TT>, or nonmember functions that take <TT>Card</TT>s and <TT>Deck</TT>s as parameters.For example, the version of <TT>find</TT> in the previous chapter takes a <TT>Card</TT> and a <TT>Deck</TT> as arguments, but you could reasonably make it a member function of either type. As an exercise, rewrite <TT>find</TT> as a <TT>Deck</TT> member function that takes a <TT>Card</TT> as a parameter.</P><P>Writing <TT>find</TT> as a <TT>Card</TT> member function is a little tricky.Here's my version:</P> <PRE>int Card::find (const Deck& deck) const { for (int i = 0; i < deck.cards.length(); i++) { if (equals (deck.cards[i], *this)) return i; } return -1;}</PRE><P>The first trick is that we have to use the keyword <TT>this</TT> to refer tothe <TT>Card</TT> the function is invoked on.</P><P>The second trick is that C++ does not make it easy to write structure definitions that refer to each other. The problem is that when the compiler is reading the first structure definition, it doesn't know about the second one yet.</P><P>One solution is to declare <TT>Deck</TT> before <TT>Card</TT> and then define <TT>Deck</TT> afterwards:</P><PRE>// declare that Deck is a structure, without defining itstruct Deck;// that way we can refer to it in the definition of Cardstruct Card{ int suit, rank; Card (); Card (int s, int r); void print () const; bool isGreater (const Card& c2) const; int find (const Deck& deck) const;};// and then later we provide the definition of Deckstruct Deck { apvector<Card> cards; Deck (); Deck (int n); void print () const; int find (const Card& card) const;};</PRE><BR><BR><H3>13.6 Shuffling</H3><P>For most card games you need to be able to shuffle the deck; that is, put the cards in a random order. In Section 10.5 we saw how to generate random numbers, but it is not obvious how to use them to shuffle a deck.</P><P>One possibility is to model the way humans shuffle, which is usually by dividing the deck in two and then reassembling the deck by choosing alternately
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -