Unit 1: Preliminaries Part 4: Introduction to the Standard Template Library Engineering 4892: Data Structures Faculty of Engineering & Applied Science Memorial University of Newfoundland May 6, 2010 ENGI 4892 (MUN) Unit 1, Part 4 May 6, 2010 1 / 19
1 The Standard Template Library 1 Review of Templates 1 vector 1 Iterators 1 Algorithms 1 Finding and Erasing ENGI 4892 (MUN) Unit 1, Part 4 May 6, 2010 2 / 19
The Standard Template Library Most high-level programming languages incorporate built-in support for a variety of data structures. For C++ this support is provided by the Standard Template Library (STL). STL is a very generic library. It is intended to be extremely flexible and to have the ability to store and operate on a wide variety of objects. To do this it makes heavy use of templates. Three of the most important constructs in STL are: Containers Iterators Algorithms In these notes we will introduce one type of container (vector), iterators, and a few algorithms. ENGI 4892 (MUN) Unit 1, Part 4 May 6, 2010 3 / 19
Review of Templates Say you happen to define a class which includes an array of 50 int s as data members, class intclass { int storage [ 5 0 ] ;... } ; Later you realize that you need a class just like intclass, but one which stores float s instead of int s. You define a new class, class floatclass { float storage [ 5 0 ] ;... } ; Having to define a brand new class for this purpose is awkward. Also, much of the... is probably common between the two classes. Having two copies of the same code is a dangerous practise.
The template feature of C++ allows us to solve this problem, template<class gentype> class genclass { gentype storage [ 5 0 ] ;... } ; We can then customize genclass with whatever type is required, genclass<int> intobject ; genclass<float> floatobject ; This type can even be a user-defined type, genclass<fred> fredobject ; However, the class Fred must properly support all of the operations that occur on it in the implementation of genclass. Usually, this requires that Fred at least... 1. copy constructs properly 2. assigns properly
Templates can be defined by more than one parameter, template<class gentype, int size = 50> class genclass { gentype storage [ size ] ;... } ; You can now define objects as follows, genclass<int> intobject1 ; // use the d e f a u l t s i z e genclass<int, 100> intobject2 ; genclass<float, 123> floatobject ; ENGI 4892 (MUN) Unit 1, Part 4 May 6, 2010 6 / 19
Templates can also be applied to individual functions, template<class gentype> void swap ( gentype& e1, gentype& e2 ) { gentype tmp = e1 ; e1 = e2 ; e2 = tmp ; } Clearly, gentype must support assignment. A user-defined class supports assignment if... It has no pointer members, thus the compiler-supplied assignment operator is sufficient, or... It has a user-defined assignment operator If swap were a member function of SomeClass, the declaration would look like this, template<class gentype> void SomeClass : : swap ( gentype& e1, gentype& e2 ) { //... as above... }
vector STL provides a number of different container classes: vector, list, slist, deque, set, multiset map, multimap stack, queue,... Here we introduce vector, A dynamic array that can hold any type of object. Any element of a vector can be accessed very quickly. This is known as random access, meaning that if you pick an element at random you can still access it very quickly. Elements can be added to the end of a vector somewhat quickly. Elements can be added anywhere else in the vector but this can be much slower. very quickly = O(1) somewhat quickly = amortized O(1) slower = O(n) (this will make sense after ch. 2)
Here is an example of creating and using a vector of int s: vector<int> vec ( 4 ) ; // d e c l a r e s i n i t i a l s i z e o f 4 vec [ 0 ] = 0 ; vec [ 1 ] = 1 5 ; vec [ 2 ] = 3 0 ; // a r r a y i n d e x i n g works vec [ 3 ] = vec [ 2 ] + vec [ 1 ] ; // on both s i d e s The vector class defines numerous other methods: int a = 6 0 ; vec. push_back ( a ) ; // v e c t o r a u t o m a t i c a l l y r e s i z e s for ( int i=0; i<(int ) vec. size ( ) ; i++ ) cout << vec [ i ] << endl ; vec. clear ( ) ; cout << "size: " << vec. size ( ) << endl ; assert ( vec. empty ( ) ) ; ENGI 4892 (MUN) Unit 1, Part 4 May 6, 2010 9 / 19
Vectors can store any type of object. vector<string> strings ; vector<fred> freds ; However, the class of object stored must satisfy the following requirements: Has a default constructor Has a destructor (if it needs one) Has a copy constructor (default may be sufficient) Has an assignment operator (default may be sufficient) For some algorithms (covered below), the == and < operators should be overloaded as well. ENGI 4892 (MUN) Unit 1, Part 4 May 6, 2010 10 / 19
Of course, to use vector we must include appropriate header file. #include <v e c t o r > using namespace std ; The STL is defined in the std namespace so the second line is usually necessary unless you want to add std:: everywhere... std : : vector<int> v ; ENGI 4892 (MUN) Unit 1, Part 4 May 6, 2010 11 / 19
Iterators Iterators are a generalization of pointers which allow you to step through (i.e. iterate) all the elements in a container. They support abstraction by leaving the container to worry about how the stepping occurs. All STL containers have an associated type of iterator. We initialize a vector called charvec and an iterator for charvec as follows: vector<char> charvec ; charvec. push_back ( a ) ; charvec. push_back ( c ) ; charvec. push_back ( e ) ; vector<char >:: iterator it ; ENGI 4892 (MUN) Unit 1, Part 4 May 6, 2010 12 / 19
An iterator must be initialized. vector provides member functions begin and end which return iterators. begin returns an iterator that is pointing at the first element of the vector. end returns an iterator that is pointing one past the last element of the vector. int count = 0 ; it = charvec. begin ( ) ; for ( ; it!= charvec. end ( ) ; it++ ) // I t e r a t o r s can be count++; // i n c r e m e n t e d // ( s e e below ) assert ( count == charvec. size ( ) ) ; As long as the iterator is properly initialized and pointing at a valid container element, it can be dereferenced just like a pointer. for ( it = charvec. begin ( ) ; it!= charvec. end ( ) ; it++ ) cout << it ; cout << endl ; This is what iterators were born to do... iterate through a container! ENGI 4892 (MUN) Unit 1, Part 4 May 6, 2010 13 / 19
As can be seen above, an iterator can be incremented just like a pointer An iterator for a vector is of the random access type, meaning that it can be incremented by any (i.e. random) amount. it = charvec. begin ( ) ; it += 2 ; cout << it << endl ; // p r i n t s e assert(++it == charvec. end ( ) ) ; The vector class provides a number of member functions which require iterators as parameters, it = charvec. begin ( ) + 1 ; charvec. insert ( it, b ) ; it += 2 ; charvec. insert ( it, d ) ; // charvec now c o n t a i n s // ( a b c d e ) See page 28 of the textbook for an alphabetical list of all of vector s member functions. ENGI 4892 (MUN) Unit 1, Part 4 May 6, 2010 14 / 19
Algorithms STL defines a number of algorithms that apply common operations to STL containers. All of these are applicable to vectors. Provided below are just a few examples. First we initialize a vector to play with, vector<int> v ( 5 ) ; v [ 0 ] = 3 ; v [ 1 ] = 9 ; v [ 2 ] = 0 ; v [ 3 ] = 9 ; v [ 4 ] = 0 ; The following replaces all of the 0 s with 7 s, replace ( v. begin ( ), v. end ( ), 0, 7 ) ; // v = (3 9 7 9 7) ENGI 4892 (MUN) Unit 1, Part 4 May 6, 2010 15 / 19
A very common operation is sorting. The following sorts v in ascending order sort ( v. begin ( ), v. end ( ) ) ; STL algorithms can often be customized by passing in the name of a function as a parameter. Assume we have defined the following, bool lessthan4 ( int x ) { return x < 4 ; } The following replaces any number in the vector that is less than 4 with 7, replace_if ( v. begin ( ), v. end ( ), lessthan4, 7 ) ; ENGI 4892 (MUN) Unit 1, Part 4 May 6, 2010 16 / 19
We can use this strategy to sort the vector in descending order bool greaterint ( int a, int b ) { return a > b ; } This function is then passed to sort, sort ( v. begin ( ), v. end ( ), greaterint ) ; ENGI 4892 (MUN) Unit 1, Part 4 May 6, 2010 17 / 19
Finding and Erasing Lets say you wanted to find a particular element in a vector. The find algorithm will return a pointer to that element (if it exists). vector<int> vec ( 4 ) ; vec [ 0 ] = 1 0 ; vec [ 1 ] = 2 0 ; vec [ 2 ] = 3 0 ; vec [ 3 ] = 4 0 ; vector<int >:: iterator pos ; pos = find ( vec. begin ( ), vec. end ( ), 3 0 ) ; ENGI 4892 (MUN) Unit 1, Part 4 May 6, 2010 18 / 19
You could erase that element with the erase member function. vec. erase ( pos ) ; If the element can t be found, find will return an iterator that is one past the end of the container. pos = find ( vec. begin ( ), vec. end ( ), 3 0 ) ; if ( pos == vec. end ( ) ) cout << " The 30 can t be found" << endl ; else cout << " The 30 is in position: " << pos vec. begin ( ) < ENGI 4892 (MUN) Unit 1, Part 4 May 6, 2010 19 / 19