Absolute C++ (4th Edition) part 79 ppt

10 241 0
Absolute C++ (4th Edition) part 79 ppt

Đang tải... (xem toàn văn)

Thông tin tài liệu

19 Standard Template Library Libraries are not made; they grow. Augustine Birrell INTRODUCTION In Chapter 17 we constructed our own versions of the stack and queue data structures. A large collection of standard structures for holding data exists. Because they are so standard it makes sense to have standard portable imple- mentations of these data structures. The Standard Template Library (STL) includes libraries for such data structures. Included in the STL are implemen- tations of the stack, queue, and many other standard data structures. When discussed in the context of the STL, these data structures are usually called container classes because they are used to hold collections of data. Chapter 7 presented a preview of the STL by describing the vector template class, which is one of the container classes in the STL. This chapter presents an overview of some of the basic classes included in the STL. Because the STL is very large, we will not be able to give a comprehensive treatment of it here, but we will present enough to get you started using some basic STL container classes as well as some of the other items in the STL. The STL was developed by Alexander Stepanov and Meng Lee at Hewlett- Packard and was based on research by Stepanov, Lee, and David Musser. It is a collection of libraries written in the C++ language. Although the STL is not part of the core C++ language, it is part of the C++ standard, and so any implementation of C++ that conforms to the standard includes the STL. As a practical matter, you can consider the STL to be part of the C++ language. As its name suggests, the classes in the STL are template classes. A typical container class in the STL has a type parameter for the type of data to be stored in the container class. The STL container classes make extensive use of iterators, which are objects that facilitate cycling through the data in a container. An introduction to the general concept of an iterator was given in Section 17.3 of Chapter 17. Although this chapter does not presuppose that you have read that section, most readers will find it helpful to read that section before reading this chapter. As defined in the STL, iterators are very general and can be used for more than just cycling through the few container classes we will discuss. Our discussion of iterators will be specialized to simple uses with the container classes discussed in this chapter. This should make the concept come alive in a concrete setting and should give you enough understanding to feel comfortable reading more advanced texts on the STL. There are numerous books dedicated to the STL. STL 19_CH19.fm Page 788 Monday, August 18, 2003 2:11 PM Iterators 789 The STL also includes implementations of many important generic algorithms, such as searching and sorting. The algorithms are implemented as template functions. After discussing the container classes, we will describe some of these algorithm imple- mentations. The STL differs from other C++ libraries, such as <iostream> for example, in that the classes and algorithms are generic , which is another way of saying that they are tem- plate classes and template functions. If you have not already done so, you should read Section 7.3 of Chapter 7, which covers the vector template class of the STL. Although the current chapter does not use any of the material in Chapter 17, most readers will find that reading Chapter 17 before this chapter will aid their comprehension of this chapter by giving sample con- crete implementations of some of the abstract ideas intrinsic to the STL. This chapter does not use any of the material in Chapters 12 to 15. Iterators To iterate is human, and programmers are human. Anonymous If you have not yet done so, you should read Chapter 10 on pointers and arrays and also read Section 7.3 of Chapter 7, which covers vectors. Vectors are one of the con- tainer template classes in the STL. Iterators are a generalization of pointers. This sec- tion shows how to use iterators with vectors. Other container template classes that we introduce in Section 19.2 use iterators in the same way. So, all that you learn about iterators in this section will apply across a wide range of containers rather than applying solely to vectors. This reflects one of the basic tenets of the STL philosophy: The semantics, naming, and syntax for iterator usage should be (and is) uniform across dif- ferent container types. ■ ITERATOR BASICS An iterator is a generalization of a pointer, and in fact is typically even implemented using a pointer, but the abstraction of an iterator is designed to spare you the details of the implementation and give you a uniform interface to iterators that is the same across different container classes. Each container class has its own iterator types, just like each data type has its own pointer type. But just as all pointer types behave essentially the same for dynamic variables of their particular data type, so too does each iterator type behave the same, but each iterator is used only with its own container type. An iterator is not a pointer, but you will not go far wrong if you think of it and use it as if it were a pointer. Like a pointer variable, an iterator variable is located at (points 19.1 iterator 19_CH19.fm Page 789 Monday, August 18, 2003 2:11 PM 790 Standard Template Library to) one data entry in the container. You manipulate iterators using the following over- loaded operators that apply to iterator objects: ■ Prefix and postfix increment operators ( ++ ) for advancing the iterator to the next data item. ■ Prefix and postfix decrement operators ( ) for moving the iterator to the previous data item. ■ Equal and unequal operators ( == and != ) to test whether two iterators point to the same data location. ■ A dereferencing operator ( * ) so that if p is an iterator variable, then *p gives access to the data located at (pointed to by) p . This access may be read only, write only, or allow both reading and changing of the data, depending on the particular container class. Not all iterators have all of these operators. However, the vector template class is an example of a container whose iterators have all these operators and more. A container class has member functions that get the iterator process started. After all, a new iterator variable is not located at (pointing to) any data in the container. Many container classes, including the vector template class, have the following mem- ber functions that return iterator objects (iterator values) that point to special data ele- ments in the data structure: ■ c.begin( ) returns an iterator for the container c that points to the “first” data item in the container c . ■ c.end( ) returns something that can be used to test when an iterator has passed beyond the last data item in a container c . The iterator c.end( ) is completely anal- ogous to NULL when used to test whether a pointer has passed the last node in a linked list of the kind discussed in Chapter 17. The iterator c.end( ) is thus an iter- ator that is not located at a data item but that is a kind of end marker or sentinel. For many container classes, these tools allow you to write for loops that cycle through all the elements in a container object c , as follows: //p is an iterator variable of the type for the container object c. for (p = c.begin( ); p != c.end( ); p++) process *p //*p is the current data item. That’s the big picture. Now lets look at the details in the concrete setting of the vector template container class. Display 19.1 illustrates the use of iterators with the vector template class. Keep in mind that each container type in the STL has its own iterator types, although they are all used in the same basic ways. The iterators we want for a vector of int s are of type std::vector<int>::iterator 19_CH19.fm Page 790 Monday, August 18, 2003 2:11 PM Iterators 791 Display 19.1 Iterators Used with a Vector 1 //Program to demonstrate STL iterators. 2 #include <iostream> 3 #include <vector> 4 using std::cout; 5 using std::endl; 6 using std::vector; 7 using std::vector<int>::iterator; 8 int main( ) 9 { 10 vector<int> container; 11 for (int i = 1; i <= 4; i++) 12 container.push_back(i); 13 cout << "Here is what is in the container:\n"; 14 iterator p; 15 for (p = container.begin( ); p != container.end( ); p++) 16 cout << *p << " "; 17 cout << endl; 18 cout << "Setting entries to 0:\n"; 19 for (p = container.begin( ); p != container.end( ); p++) 20 *p = 0; 21 cout << "Container now contains:\n"; 22 for (p = container.begin( ); p != container.end( ); p++) 23 cout << *p << " "; 24 cout << endl; 25 return 0; 26 } S AMPLE D IALOGUE Here is what is in the container: 1 2 3 4 Setting entries to 0: Container now contains: 0 0 0 0 19_CH19.fm Page 791 Monday, August 18, 2003 2:11 PM 792 Standard Template Library Another container class is the list template class. Iterators for list s of int s are of type std::list<int>::iterator In the program in Display 19.1 we specialize the type name iterator so it applies to iterators for vectors of int s. The type name iterator that we want in Display 19.1 is defined in the template class vector . Thus, if we specialize the template class vector to int s and want the iterator type for vector<int> , we want the type vector<int>::iterator; Because the vector definition places the name vector in the std namespace, the entire using declaration is as follows: using std::vector<int>::iterator; The basic use of iterators with vector (or any container class) is illustrated by the following lines from Display 19.1: iterator p; for (p = container.begin( ); p != container.end( ); p++) cout << *p << " "; Recall that container is of type vector<int>, and that the type iterator really means vector<int>::iterator. A vector v can be thought of as a linear arrangement of its data elements. There is a first data element v[0], a second data element v[1], and so forth. An iterator p is an object that can be located at one of these elements (think “points to” one of these ele- ments). An iterator can move its location from one element to another element. If p is located at, say, v[7], then p++ moves p so it is located at v[8]. This allows an iterator to move through the vector from the first element to the last element, but it needs to find the first element and needs to know when it has seen the last element. You can tell if an iterator is at the same location as another iterator by using the operator, ==. Thus, if you have an iterator pointing to the first, last, or other element, you could test another iterator to see if it is located at the first, last, or other element. If p1 and p2 are two iterators, then the comparison p1 == p2 is true when and only when p1 and p2 are located at the same element. (This is analo- gous to pointers. If p1 and p2 were pointers, this comparison would be true if they pointed to the same thing.) As usual, != is just the negation of ==, and so p1!= p2 is true when p1 and p2 are not located at the same element. 19_CH19.fm Page 792 Monday, August 18, 2003 2:11 PM Iterators 793 The member function begin( ) is used to position an iterator at the first element in a container. For vectors, and many other container classes, the member function begin( ) returns an iterator located at the first element. (For a vector v the first element is v[0].) Thus, iterator p = v.begin( ); initializes the iterator variable p to an iterator located at the first element. The basic for loop for visiting all elements of the vector v is therefore iterator p; for (p = v.begin( ); Boolean_Expression ; p++) Action_At_Location p; The desired stopping condition is p = v.end( ) The member function end( ) returns a sentinel value that can be checked to see if an iterator has passed the last element. If p is located at the last element, then after p++, the test p = v.end( )changes from false to true. So the correct Boolean_Expression is the negation of this stopping condition: iterator p; for (p = v.begin( ); p != v.end( ); p++) Action_At_Location p; Note that p != v.end( ) does not change from true to false until after p’s location has advanced past the last element. So, v.end( ) is not located at any element. The value v.end( ) is a special value that serves as a sentinel. It is not an iterator, but you can compare v.end( ) to an iterator using == and !=. The value v.end( ) is analogous to the value NULL that is used to mark the end of a linked list of the kind discussed in Chapter 17. The following for loop from Display 19.1 uses this same technique with the vector named container: iterator p; for (p = container.begin( ); p != container.end( ); p++) cout << *p << " "; The action taken at the location of the iterator p is cout << *p << " "; The dereferencing operator, *, is overloaded for STL container iterators so that *p pro- duces the element at location p. In particular, for a vector container, *p produces the element located at the iterator p. The above cout statement thus outputs the element located at the iterator p, and so the entire for loop outputs all the elements in the vec- tor container. begin( ) end( ) 19_CH19.fm Page 793 Monday, August 18, 2003 2:11 PM 794 Standard Template Library Self-Test Exercises The dereferencing operator *p always produces the element located at the iterator p. In some situations *p produces read-only access, which does not allow you to change the element. In other situations it gives you access to the element and will let you change the element. For vectors, *p will allow you to change the element located at p, as illustrated by the following for loop from Display 19.1: for (p = container.begin( ); p != container.end( ); p++) *p = 0; This for loop cycles through all the elements in the vector container and changes all the elements to 0. 1. If v is a vector, what does v.begin( ) return? What does v.end( ) return? 2. If p is an iterator for a vector object v, what is *p? 3. Suppose v is a vector of ints. Write a for loop that will output all the elements of p except for the first element. I TERATOR An iterator is an object that can be used with a container to gain access to elements in the con- tainer. An iterator is a generalization of the notion of a pointer. The operators ==, !=, ++, and behave the same for iterators as they do for pointers. The basic outline of how an iterator can cycle through all the elements in a container is as follows: iterator p; for (p = container.begin( ); p != container.end( ); p++) Process_Element_At_Location p; The member function begin( ) returns an iterator located at the first element. The member func- tion end( ) returns a value that serves as a sentinel value one location past the last element in the container. D EREFERENCING The dereferencing operator, *p, when applied to an iterator p, produces the element located at the iterator p. In some situations *p produces read-only access, which does not allow you to change the element. In other situations it gives you access to the element and will let you change the element. 19_CH19.fm Page 794 Monday, August 18, 2003 2:11 PM Iterators 795 ■ KINDS OF ITERATORS Different containers have different kinds of iterators. Iterators are classified according to the kinds of operations that work on them. Vector iterators are of the most general form; that is, all the operations work with vector iterators. Thus, we will again use the vector container to illustrate iterators. In this case we use a vector to illustrate the itera- tor operations of decrement and random access. Display 19.2 shows another program using a vector object named container and an iterator p. The decrement operator is used on line 30 of Display 19.2. As you would expect, p moves the iterator p to the previous location. The decrement operator, , is similar to the increment operator, ++, but it moves the iterator in the opposite direction. Display 19.2 Bidirectional and Random-Access Iterator Use (part 1 of 2) 1 //Program to demonstrate bidirectional and random-access iterators. 2 #include <iostream> 3 #include <vector> 4 using std::cout; 5 using std::endl; 6 using std::vector; 7 using std::vector<char>::iterator; 8 int main( ) 9 { 10 vector<char> container; 11 container.push_back(’A’); 12 container.push_back(’B’); 13 container.push_back(’C’); 14 container.push_back(’D’); 15 for (int i = 0; i < 4; i++) 16 cout << "container[" << i << "] == " 17 << container[i] << endl; 18 iterator p = container.begin( ); 19 cout << "The third entry is " << container[2] << endl; 20 cout << "The third entry is " << p[2] << endl; 21 cout << "The third entry is " << *(p + 2) << endl; 22 cout << "Back to container[0].\n"; 23 p = container.begin( ); 24 cout << "which has value " << *p << endl; 25 cout << "Two steps forward and one step back:\n"; 26 p++; 27 cout << *p << endl; Three different notations for the same thing This notation is specialized to vectors and arrays. These two work for any random- access iterator. 19_CH19.fm Page 795 Monday, August 18, 2003 2:11 PM 796 Standard Template Library The increment and decrement operators can be used in either prefix (++p) or postfix ( p++) notation. In addition to changing p, they also return a value. The details of the value returned are completely analogous to what happens with the increment and dec- rement operators on int variables. In prefix notation, first the variable is changed and then the changed value is returned. In postfix notation, the value is returned before the variable is changed. We prefer not to use the increment and decrement operators as expressions that return a value; we use them only to change the variable value. The following lines from Display 19.2 illustrate the fact that with vector iterators you have random access to the elements of a vector, such as container: iterator p = container.begin( ); cout << "The third entry is " << container[2] << endl; cout << "The third entry is " << p[2] << endl; cout << "The third entry is " << *(p + 2) << endl; Random access means that you can go directly to any particular element in one step. We have already used container[2] as a form of random access to a vector. This is sim- ply the square bracket operator that is standard with arrays and vectors. What is new is that you can use this same square bracket notation with an iterator. The expression p[2] is a way to obtain access to the element indexed by 2. Display 19.2 Bidirectional and Random Access Iterator Use (part 2 of 2) 28 p++; 29 cout << *p << endl; 30 p ; 31 cout << *p << endl; 32 return 0; 33 } S AMPLE D IALOGUE container[0] == A container[1] == B container[2] == C container[3] == D The third entry is C The third entry is C The third entry is C Back to container[0]. which has value A Two steps forward and one step back: B C B This works for any bidirectional iterator. random access 19_CH19.fm Page 796 Monday, August 18, 2003 2:11 PM Iterators 797 The expressions p[2] and *(p + 2) are completely equivalent. By analogy to pointer arithmetic (see Chapter 10), (p + 2) names the location two places beyond p. Since p is at the first (index 0) location in the previous code, (p + 2) is at the third (index 2) loca- tion. The expression (p + 2) returns an iterator. The expression *(p + 2) dereferences that iterator. Of course, you can replace 2 with a different nonnegative integer to obtain a pointer to a different element. Be sure to note that neither p[2] nor (p + 2) changes the value of the iterator in the iterator variable p. The expression (p + 2) returns another iterator at another location, but it leaves p where it was. Something similar happens with p[2] behind the scenes. Also note that the meaning of p[2] and (p + 2) depends on the location of the iterator in p. For example, (p + 2) means two locations beyond the location of p, wherever that may be. For example, suppose the previously discussed code from Display 19.2 were replaced with the following (note the added p++): iterator p = container.begin( ); p++; cout << "The third entry is " << container[2] << endl; cout << "The third entry is " << p[2] << endl; cout << "The third entry is " << *(p + 2) << endl; The output of these three couts would no longer be The third entry is C The third entry is C The third entry is C but would instead be The third entry is C The third entry is D The third entry is D The p++ moves p from location 0 to location 1, and so (p + 2) is now an iterator at location 3, not location 2. So, *(p + 2) and p[2] are equivalent to container[3], not container[2]. We now know enough about how to operate on iterators to make sense of how iter- ators are classified. The main kinds of iterators are as follows. Forward iterators: ++ works on the iterator. Bidirectional iterators: Both ++ and work on the iterator. Random-access iterators: ++, , and random access all work with the iterator. Note that these are increasingly strong categories: Every random-access iterator is also a bidirectional iterator, and every bidirectional iterator is also a forward iterator. As we will see, different template container classes have different kinds of iterators. The iterators for the vector template class are random-access iterators. 19_CH19.fm Page 797 Monday, August 18, 2003 2:11 PM . a collection of libraries written in the C++ language. Although the STL is not part of the core C++ language, it is part of the C++ standard, and so any implementation of C++ that conforms to the standard. conforms to the standard includes the STL. As a practical matter, you can consider the STL to be part of the C++ language. As its name suggests, the classes in the STL are template classes. A typical container. int s are of type std::vector<int>::iterator 19_CH19.fm Page 790 Monday, August 18, 2003 2:11 PM Iterators 791 Display 19.1 Iterators Used with a Vector 1 //Program to demonstrate

Ngày đăng: 04/07/2014, 05:21

Tài liệu cùng người dùng

Tài liệu liên quan