Presentation is loading. Please wait.

Presentation is loading. Please wait.

Department of Computer Science and Engineering, HKUST 1 HKUST Summer Programming Course 2008 Standard Template Library (STL) ~ Generic Programming.

Similar presentations


Presentation on theme: "Department of Computer Science and Engineering, HKUST 1 HKUST Summer Programming Course 2008 Standard Template Library (STL) ~ Generic Programming."— Presentation transcript:

1 Department of Computer Science and Engineering, HKUST 1 HKUST Summer Programming Course 2008 Standard Template Library (STL) ~ Generic Programming

2 2 Overview  Introduction An Overview of the STL Containers Iterators Algorithms  Introduction to Container Classes  STL Container Classes Sequence Containers – vector, list, deque  Introduction to Iterators  STL Iterators Operations on Iterators Understanding end()

3 3 Overview  More about STL Containers Associative Containers – multimap, map Container Adaptors – stack, queue, priority_queue  Introduction to STL Algorithms sort() find(), find_if() for_each() count_if() transform(), copy()  Function Pointers  Function Objects

4 Department of Computer Science and Engineering, HKUST 4 Standard Template Library Introduction

5 5  This note explores what is considered to be the most important new features added to C++ in recent years: The Standard Template Library (STL)  STL provides general-purpose, templatized classes and functions that implement many popular and commonly used algorithms and data structures.  Because the STL is constructed from template classes, the algorithms and data structures can be applied to nearly any type of data.

6 6 An Overview of the STL  At the core of the standard template library are three foundational items: Containers Iterators Algorithms  These items work in conjunction with one another to provide the off-the-shelf solutions to a variety of programming problems.

7 7 Containers (Introduction)  Container means class that holds and manipulates a number of objects and we call it container class.  There are several different types, for example, vector class defines a dynamic array, deque class creates a double-ended queue list class provides a linear list.  These containers are called sequence containers because in STL terminology, a sequence is a linear list.

8 8 Containers (Introduction)  In addition to the basic containers, the STL also defines associative containers, which allow efficient retrieval of values based on keys. For example, a map provides access to values with unique keys. Thus a map stores a key/value pair and allows a value to be retrieved given its key.  Each container class defines a set of functions that can be applied to the container, for example A list container includes functions that insert, delete, and merge elements. A stack include functions that push and pop values.

9 9 Iterators (Introduction)  Iterators are objects that are, more or less, like pointers.  They give you the ability to cycle through the contents of a container in much the same way that you would use a pointer to cycle through an array.  Iterators are handled just like pointers. You can increment and decrement them. You can apply the * operator to them.  Iterators are declared using iterator type defined by various containers.

10 10 Algorithms (Introduction)  Algorithms act on containers.  They provide the means by which you will manipulate the contents of containers.  Their capabilities include initialization, sorting, searching, and transforming the contents of containers.  Many algorithms operate on a range of elements within a container.

11 Department of Computer Science and Engineering, HKUST 11 Standard Template Library Introduction to Container Classes Remark: The vector type in STL is better than the classes we’ll write in this section, so this is just for understanding. We are doing this to illustrate how C++’s actual vector, list, etc. can be implemented.

12 12 Container Classes  Container classes are a typical use for class templates, since we need container classes for objects of many different types, and the types are not known when the container is designed.  To have a better understanding on how container classes work, let’s design a container that looks like an array which is a first class type: so that assignment and call-by-value is possible. a homogeneous container: all the elements must be the same type.

13 13 Example – Bunch Container Class // bunch.h template class Bunch{ public: Bunch(); Bunch(const Bunch& B); ~Bunch(); int size() const{ return N; } T& operator[](int i}{ return value_m[i]; } T& operator=(const Bunch& B); private: T value_m[N]; };

14 14 Example – Bunch Container Class #include “bunch.h” int main(){ Bunch a; cout << a[3]; a[7] = 13; ++a[2]; Bunch b; b[49] = “Hello world”; Bunch c; c = b;// Legal Bunch d; d = a;// Error: d and a are of different types return 0; }

15 15 Example – Array Container Class // array.h template class Array{ private: T* value_m; int size_m; public: Array(int n=10);// default/type Conversion constructor Array(const Array& A);// copy Constructor ~Array();// destructor int size() const{ return size_m; } // assignment operator Array & operator=(const Array & A); // access to an element T& operator[](int i){ return value_m[i]; } // const access to an element const T& operator[](int i) const { return value_m[i]; } };

16 16 Example – Array Container Class template Array ::Array(int n): value_m(new T[n]), size_m(n) {} template Array ::Array(const Array & A) : value_m( new T[A.size_m]), _size(A.size_m){ for(int i=0; i<size_m; ++i) value_m[i] = A.value_m[i]; } template Array ::~Array(){ delete [] value_m; }

17 17 Example – Array Container Class #include #include “array.h” using namespace std; int main(){ Array a; cout << a.size() << endl; a[9] = 17;// OK: uses non-const version of operator[] ++a[2];// OK: uses non-const version of operator[] cout << a[2] << endl; Array b(5); cout << b.size() << endl; const Array c(20); c[1] = 5;// Error: assignment to read-only location cout << c[1] << endl; a = c; cout << a[2] << endl; return 0; }

18 18 Shallow Copy and Deep Copy Array A(10); Array B(A);  Shallow Copy: If you don’t define your own copy constructor, the copy constructor provided by the compiler simply does member- wise copy. Then A and B will share to the same value_m array. If you delete A, and then B, you will have an error as you will delete the embedded value_m array twice from the heap.

19 19 Shallow Copy and Deep Copy  Basically, shallow copy is a bad idea if an object owns data.  Deep Copy: To take care of the ownership, redefine the copy constructor so that each object has its own copy of the “owned” data members.

20 20 Assignment Operator  Idea: To assign b = a, first throw away the old data b.value_m, then create a new one and assign the elements from a.value_m. template Array & Array ::operator=(const Array & A){ delete [] value_m; size_m = A.size_m; value_m = new T[size_m]; for(int i=0; i<size_m; ++i) value_m[I] = A.value_m[i]; return *this. }

21 21 Assignment Operator  There is a serious problem with the previous code.  In the assignment a=a, the data in the container is lost!  Solution: When the assignment argument is the same as the object being assigned to, don’t do anything. template Array & Array ::operator=(const Array & A){ if(this != &A){ delete [] value_m; size_m = A.size_m; value_m = new T[size_m]; for(int i=0; i<size_m; ++i) value_m[i] = A.value_m[i]; } return *this. }

22 22 Output Operator as Global Function  The following output operator is not a member of the Array class, but a function template.  Function templates and class templates work together very well: We can use function template to implement functions that will work on any class created from a class template. template ostream& operator & A){ os << “#elements stored = “ << x.size() << endl; for(int i=0; i<A.size(); ++i) os << A[i] << ‘ ’; return os; }

23 23 Why 2 Different Subscript Operators?  We have 2 subscript operators, and it looks as if we are violating the overloading rule. Both have the same name and the same arguments. Array a(3); a[2] = 7; // Which version of operator[] is called?  In the above code, we need a subscript operator that return an int&, not a const int&.  But this subscript operator does not work in this code: int last_element(const Array & a){ return a[a.size()-1]; }

24 24 Why 2 Different Subscript Operators?  The argument “ a ” of last_element() is a const Array &.  Therefore it can only call const member functions: in this example, int size() const const T& operator[](int) const  Note: On the other hand, if programmers are not so strict with const correctness (which is a bad idea), they could simply define one subscript function as // This is dangerous! (Why?) T& operator[](int i) const { return value_m[i]; }

25 Department of Computer Science and Engineering, HKUST 25 Standard Template Library STL Container Classes

26 26 STL Sequence Containers  Some homogeneous container classes commonly used to represent a sequence of objects of the same type are as follows. vector : one-dimensional array  Fast access at any position.  Add and remove elements only at the back. list : doubly-linked list  Fast access to front and back only.  Add and remove elements at any position. deque : double-ended array  Fast access at any position.  Add and remove elements only at the front and back.

27 27 Standard Sequence Containers  They differ in how quickly different access operations can be performed. n is the number of elements currently in the container. O(1) means constant time. ContainerAccess/RetrievalInsert,Erase vector (1D array) O(1) random access O(1) at back only O(n) at front, in middle list (doubly-linked list) O(1) at front/back only No random access (would be O(n)) O(1) at any position deque (doubly-ended queue) O(1) random access O(1) at front/back only O(n) in middle

28 28 Example – STL Container Classes #include using namespace std; int main(){ vector vd; deque ds; list li; vd.push_back(5.0); vd_push_back(10.0); vd.push_back(15.0); cout << vd[1] << endl; ds.push_back(“World”); ds.push_front(“Hello ”); cout << ds[0] << ds[1] << endl; li.push_back(1); li.push_back(2); cout << li[0]; // error: list doesn’t support subscript operator [] return 0; }

29 29 Sequence Containers: Access, Add, Remove  Element access for all: front() : First element back() ; Last element  Element access for vector and deque : [ ] : Subscript operator, index not checked.  Add/remove elements for all: push_back() : Append element. pop_back() : Remove last element.  Add/remove elements for list and deque : push_front() : Insert element at the front. pop_front() : Remove first element

30 30 Sequence Container: Other Operations  Miscellaneous operations for all: size() : Returns the number of elements. empty() : Returns true if the sequence is empty. resize(int i) : Change size of the sequence.  Comparison operators == != < etc, are also defined. i.e., you can compare if two containers are equal.  “List” operations are fast for list, but also available for vector and deque : insert(p,x) : Insert an element at a given condition. erase(p) : Remove an element. clear() : Erase all elements. What is p ? p is an iterator.

31 Department of Computer Science and Engineering, HKUST 31 Standard Template Library Introduction to Iterators

32 32 Print with an Array  Before talking about the details of iterator, let’s take a look at an example on how pointer works.  Suppose we have an array and we would like to print each element in the array, const int LEN = 10; int x[LEN]; int* const x_end = &x[LEN]; for(int* p=x; p<=x_end; ++p) cout << *p;

33 33 Print with an Array  We use an int pointer to access the elements of an integer sequence with some basic operations: OperationGoal p=x Initialize to the beginning of an array *p Access the element being pointed to ++p Point to the next element p!=x_end Compare with the end of an array

34 34 Printing a List Sequentially  Similarly, to process list elements sequentially, one may define p as an iterator list ::iterator, and use functions begin() and end() to get iterators pointing to the beginning and end of container. #include // “list” class of STL using namespace std; int main(){ list x; list ::iterator p; for(p = x.begin(); p!= x.end(); ++p) cout << *p; return 0; }

35 35 Printing a Vector Sequentially  One can similarly define an iterator vector ::iterator to sequentially go through items in a vector. #include // “vector” class of STL using namespace std; int main(){ vector x; vector ::iterator p; for(p=x.begin(); p!=x.end(); ++p) cout << *p; return 0; }

36 Department of Computer Science and Engineering, HKUST 36 Standard Template Library STL Iterators

37 37 Iterators  For each kind of container in the STL there is an iterator type. list ::iterator ip; vector<string::iterator vp; deque ::iterator dp;  Iterators are a generalization of pointers, and are used much like pointers: They can be used to indicate elements in the sequence. A pair of iterators can be used to indicate a subsequence.

38 38 Iterators  Operations on iterators are just like pointers in arrays: Access element: *pp-> Go to next or previous elements: ++p --p Compare iterators: == !=  Iterators are very useful when accessing the contents of a container. For example, to cycle through a container, you can obtain an iterator to its beginning using begin() and then increment that iterator until its value is equal to end().

39 39 #include using namespace std; void display(list ::iterator first, list ::iterator end){ list ::iterator p; for(p=first; p!=end; ++p) cout << *p << “ ”; } int main(){ list my_list, small, large; list ::iterator ip; for(int i=1; i<13; ++i) my_list.push_back(i*i%13); for(ip=my_list.begin(); ip<=my_list.end(); ++ip){ if(*ip<7) small.push_back(*ip); else large.push_back(*ip); } cout << “my_list: ”; display(my_list.begin(), my_list.end()); cout << endl; cout << “small: ”; display(small.begin(), small.end()); cout << endl; cout << “large: ”; display(large.begin(), large.end()); cout << endl; return 0; }

40 40 Understanding end()  end() has an unexpected attribute which is counterintuitive. In the first glance, it looks like returning a pointer to the last element in the container, but it’s actually not. Instead, it returns a pointer one past the last element. Thus, the last element in a container is pointed to by end()- 1.  For example, consider the following program, which displays a list forward and backward.

41 41 // Understanding end() #include using namespace std; int main(){ list l; // create an empty list for(int i=0; i<10; i++) l.push_back(i); cout << “List printed forwards:” << endl; list ::iterator p = l.begin(); while(p != l.end()){ cout << *p << “ ” ; p++; } cout << endl << endl; cout << “List printed backwards:” << endl; p = l.end(); while(p != l.begin()){ p--; // decrement pointer before using cout << *p << “ ”; } return 0; } Output: List printed forwards: 0 1 2 3 4 5 6 7 8 9 List printed backwards: 9 8 7 6 5 4 3 2 1 0

42 42 Understanding end()  In this example, we need to pay attention to the code that displays the list in reverse order.  The iterator p is initially set to the end of the list through the use of the end() function.  Since end() returns an iterator to an object that is one past the last object actually stored in the list, p must be decrement before it is used. This is why p is decremented before the cout statement inside the loop, rather than after.

43 Department of Computer Science and Engineering, HKUST 43 Standard Template Library More about STL containers

44 44 STL Associative Containers  In addition to the basic containers, the STL also defines associative containers, which allow efficient retrieval of values based on keys.  Some associative container classes commonly used are as follows. multimap : multiple associative container  No limit on the number of elements with the same key. map : pair associative container  No two elements have the same key.

45 45 multimap  Fast storage and retrieval of keys and associated values. Has key/value pairs  Duplicate keys allowed multiple values for a single key One-to-many relationship (e.g., one student can take many courses)  To use it, we must write #include in our program.  Insert pair objects (with a key and value)  Declarations multimap myMultiMap;

46 46 #include using namespace std; typedef multimap mmintdouble; int main(){ mmintdouble pairs; cout << "There are currently " << pairs.count(16) << " pairs with key 16 in the multimap" << endl; pairs.insert(mmintdouble::value_type(16, 3.2)); pairs.insert(mmintdouble::value_type(16, 33.2)); cout << "After inserts, there are " << pairs.count(16) << " pairs with key 16" << endl << endl; pairs.insert(mmintdouble::value_type(30, 1.1)); pairs.insert(mmintdouble::value_type(10, 2.1)); pairs.insert(mmintdouble::value_type(25, 3.1)); pairs.insert(mmintdouble::value_type(20, 9.2)); pairs.insert(mmintdouble::value_type(5, 7.5)); for(mmintdouble::const_iterator iter = pairs.begin(); iter != pairs.end(); ++iter) cout first second << endl; cout << endl; return 0; } Output: There are currently 0 pairs with key 16 in the multimap After inserts, there are 2 pairs with key 16 5 7.5 10 2.1 16 3.2 16 33.2 20 9.2 25 3.1 30 1.1

47 47 map  Like multimap, but only unique key/value pairs One-to-one mapping (duplicates ignored)  Able to use [] to access values  To use it, we must write #include in our program.  If subscript not in map, creates new key/value pair.  Declarations map myMap;

48 48 #include using namespace std; typedef map mintdouble; int main(){ mintdouble pairs; pairs.insert(mintdouble::value_type(15, 3.2)); pairs.insert(mintdouble::value_type(30, 33.2)); pairs.insert(mintdouble::value_type(5, 1.1)); pairs.insert(mintdouble::value_type(10, 2.1)); pairs.insert(mintdouble::value_type(25, 3.1)); pairs.insert(mintdouble::value_type(5, 9.2)); // duplicate ignored pairs.insert(mintdouble::value_type(15, 7.5)); // duplicate ignored cout << "Pairs contains:\nKey\tValue\n"; for(mintdouble::const_iterator iter = pairs.begin(); iter != pairs.end(); ++iter) cout first second << endl; // use subscript operator to change value for key 25 pairs[25] = 1234.56; // use subscript operator to insert value for key 40 pairs[40] = 8765.43; cout << "\nAfter subscript operator, pairs contains:\nKey\tValue\n"; for(mintdouble::const_iterator iter = pairs.begin(); iter != pairs.end(); ++iter) cout first second << endl; cout << endl; return 0; } Output: Pairs contains: Key Value 5 1.1 10 2.1 15 3.2 25 3.1 30 33.2 After subscript operator, pairs contains: Key Value 5 1.1 10 2.1 15 3.2 25 1234.56 30 33.2 40 8765.43

49 49 STL Container Adapters  STL also support a number of container adapters. For example: stack queue priority_queue  They are not the first class containers. That means they are not container by itself, but using existing containers (e.g. vector, list or deque) to simulate the properties of some famous data structures. Note: They do not suppose iterator.

50 50 stack Adapter  A stack is a last-in, first-out (LIFO) data structure. That is the element at the top of a stack is the one that was most recently added. Can use vector, list or deque (default) as underlying container. To use it, we must write #include in our program.  Declarations stack > myStack; stack > myOtherStack; stack anotherStack; // default deque

51 51 #include using namespace std; template void popElements(T& stackRef){ while(!stackRef.empty()){ cout << stackRef.top() << ‘ ’; stackRef.pop(); } int main(){ stack intDequeStack; stack > intVectorStack; stack > intListStack; for(int i=0; i<10; i++){ intDequeStack.push(i); intVectorStack.push(i); intListStack.push(i); } cout << “Popping from intDequeStack: ”; popElements(intDequeStack); cout << endl; cout << “Popping from intVectorStack: ”; popElements(intVectorStack); cout << endl; cout << “Popping from intListStack: ”; popElements(intListStack); cout << endl; return 0; } Output: Popping from intDequeStack: 9 8 7 6 5 4 3 2 1 0 Popping from intVectorStack: 9 8 7 6 5 4 3 2 1 0 Popping from intListStack: 9 8 7 6 5 4 3 2 1 0

52 52 queue Adapter  A queue is a first-in, first-out (FIFO) data structure. That is, elements are added to the back of the queue and may be removed from the front. Can use list or deque (default) as underlying container. To use it, we must write #include in our program.  Declarations queue > myQueue; queue myOtherQueue; // default deque

53 53 #include using namespace std; template void popElements(T& queueRef){ while(!queueRef.empty()){ cout << queueRef.front() << ‘ ’; queueRef.pop(); } int main(){ queue intDequeQueue; queue > intListQueue; for(int i=0; i<10; i++){ intDequeQueue.push(i); intListQueue.push(i); } cout << “Popping from intDequeQueue: ”; popElements(intDequeQueue); cout << endl; cout << “Popping from intListQueue: ”; popElements(intListQueue); cout << endl; return 0; } Output: Popping from intDequeQueue: 0 1 2 3 4 5 6 7 8 9 Popping from intListQueue: 0 1 2 3 4 5 6 7 8 9

54 54 priority_queue Adapter  A data structure that efficiently supports finding the item with the highest priority. Highest priority (largest) element always removed first. Can use vector (default) or deque as underlying container. To use it, we must write #include in our program.  Declarations priority_queue myPriorityQueue; // default vector priority_queue > anotherPriorityQueue;

55 55 #include using namespace std; template void popElements(T& priQueueRef){ while(!priQueueRef.empty()){ cout << priQueueRef.top() << ' '; priQueueRef.pop(); } int main(){ priority_queue intVectorPriQueue; priority_queue > intDequePriQueue; for(int i=0; i<10; i++){ intVectorPriQueue.push(i); intDequePriQueue.push(i); } cout << "Popping from intVectorPriQueue: "; popElements(intVectorPriQueue); cout << endl; cout << "Popping from intDequePriQueue: "; popElements(intDequePriQueue); cout << endl; return 0; } Output: Popping from intVectorPriQueue: 9 8 7 6 5 4 3 2 1 0 Popping from intDequePriQueue: 9 8 7 6 5 4 3 2 1 0

56 56 More about STL Containers  Today, all modern C++ compilers include at least a basic implementation of STL.  Beware: some implementations still do not fully support all the specifications in the Standard C++ library.  The class standard reference for STL (including the extended SGI version of STL): http://www.sgi.com/tech/stl/

57 Department of Computer Science and Engineering, HKUST 57 Standard Template Library Introduction to STL Algorithms

58 58 STL Algorithms  The Standard Template Library not only contains container classes, but also algorithms that operate on sequence containers. To use them, we must write #include in our program.  In this section, we will see a few different algorithms contained in the STL sort() (with and without explicit comparator functions) find(), find_if() for_each() count_if() transform(), copy()

59 59 STL Algorithm – sort()  Let vector A ; for some class T.  Let vector ::iterator p, q  sort(p,q) sorted A between p and q  Common case is sort(A.begin(), A.end()) sorts all of A.  sort() also works with deque objects but not with list objects.  In general, sort() works with any random access sequence container.

60 60 // sort without comparators #include Using namespace std; template void Display(Iterator start, Iterator end){ for(Iterator p=start; p!=end; ++p) cout << *p << “ ”; } int main(){ vector composer; composer.push_back(“Mozart”); composer.push_back(“Bach”); composer.push_back(“Chopin”); composer.push_back(“Beethoven”); cout << “composer: ”; Display(composer.begin(), composer.end()); cout << endl; sort(composer.begin(), composer.end()); cout << “composer: ”; Display(composer.begin(), composer.end()); cout << endl; vector v; for(int i=1; i<13; i++) v.push_back(i*I%13); cout << “v: ”; Display(v.begin(), v.end()); cout << endl; sort(v.begin(), v.end()); cout << “v: “; Display(v.begin(), v.end()); cout << endl; return 0; } Output: composer: Mozart Bach Chopin Beethoven Composer: Bach Beethoven Chopin Mozart v: 1 4 9 3 12 10 10 12 3 9 4 1 v: 1 1 3 3 4 4 9 9 10 10 12 12

61 61 STL Algorithm – find() #include int main(){ list composer; composer.push_back(“Mozart”); composer.push_back(“Bach”); composer.push_back(“Chopin”); composer.push_back(“Beethoven”); list ::iterator p; p=find(composer.begin(), composer.end(), “Bach”); if(p == composer.end()) cout << “Not found.” << endl; else if(++p != composer.end()) cout << “Found before: ” << *p << endl; else cout << “Found at the end.” << endl; return 0; }

62 62 Algorithms, Iterators and Sub-sequences  Sequences/Sub-sequences are specified using iterators that indicate the beginning and the end for an algorithm to work on.  Here we find the 2 nd occurrence of the value, 341, in a sequence. int f(int x){ return –x*x + 40*x + 22; } // 22 61 98 133 166 197 226 253 278 301 322 341 358 373 386 397 // 406 413 418 421 422 421 418 413 406 397 386 373 358 341 322 301 template void my_initialization(T& x){ const int N = 39; for(int j=0; j<N; ++j) x.push_back(f(j)); }

63 63 Example #include int main(){ const int search_value = 341; vector x; my_initialization(x); vector ::iterator p; p = find(x.begin(), x.end(), search_value); if(p != x.end()){// Value found! p = find(++p, x.end(), search_value);// Find again if(p != x.end())// Value found again! cout << “Found after: “ << *--p << endl; } return 0; }

64 64 STL find() - Implementation template IteratorT find(IteratorT first, IteratorT last, const T& value){ while(first != last && *first != value) ++first; return first; }  find() searches linearly through a sequence, and stops when an item matches the 3 rd argument.  A big limitation of find() is that it requires an exact match by value.

65 Department of Computer Science and Engineering, HKUST 65 Standard Template Library Function Pointers

66 66 Function Pointers  Function pointers are the key to removing this limitation. This dramatically increases the power of STL’s generic algorithms.  We will learn the basics of function pointers.  Later, we will learn about an even more powerful generalization called function objects.

67 67 Generic Algorithms with Function Arguments  Let’s search in a container for a value that satisfies a boolean condition specified by a C++ function. #include using namespace std; bool greater_than_350(int value) { return value > 350; } int main(){ vector x; my_initialization(x); vector ::iterator p = find_if(x.begin(), x.end(), greater_than_350); if(p != x.end()) cout << “Found element: “ << *p << endl; return 0; }

68 68 STL Algorithms – find_if() template IteratorT find_if(IteratorT first, IteratorT last, PredicateT pred){ while(first != last && !pred(*first)) ++first; return first; }  find_if() is a more general algorithm than find() in that it stops when a condition is satisfied.  This allows partial match, or match by keys.  The condition is specified by a function.

69 69 C Function Pointer  C++ allows a function to be passed as argument to another function. (This is a tradition that is inherited from C.)  What’s actually happening is that we pass a function pointer: a pointer to the memory address of the executable code.  e.g. if you type man 3 qsort on Linux, you’ll see: void qsort(void* base, size_t nmemb, size_t size, int(*compare)(const void*, const void*))

70 70 C Function Pointer This means the 4 th argument, compare, is a function pointer, whose type is int(*)(const void*, const void*)  For example, you could pass a pointer to the following function: int compare_floats(float* i, float* j){ return (*i) – (*j); }

71 71 Example: C Function Pointer #include using namespace std; int max(int x, int y){ return (x>y) ? x:y; } int min(int x, int y){ return (x>y) ? y:x; } int main(){ int (*f)(int x, int y); int choice; cin >> choice; if(choice == 1) f = max; else f = min; cout << f(3,5) << endl; return 0; }

72 72 STL sort() - Again  The STL sort() function seen before can actually accept a function as its third argument: template void sort(IteratorT first, IteratorT last, PredicateT pred)  It sorts everything between first and last using the predicate pred as a comparison function.

73 73 #include using namespace std; class Person{ public: string name; int id; Person(string n, int i) : name(n), id(1) {} }; void display(Vector & people){ vector ::iterator p; for(p = people.begin(); p != people.end(); ++p) cout << “(“ << (*p).name << “,” << (*p).id << “)”; cout << endl; } bool It_name(const Person& p1, const Person& p2){ return (p1.name < p2.name); }

74 74 bool It_id(const Person& p1, const Person& p2){ return (p1.id < p2.id); } int main(){ vector people; people.push_back(Person(“K”,20)); people.push_back(Person(“G”,60)); people.push_back(Person(“W”,50)); people.push_back(Person(“S”,40)); people.push_back(Person(“T”,35)); display(people); sort(people.begin(), people.end(), It_name); display(people); sort(people.begin(), people.end(), It_id); display(people); return 0; } Output: (K,20) (G,60) (W,50) (S,40) (T,35) (G,60) (K,20) (S,40) (T,35) (W,50) (K,20) (T,35) (S,40) (W,50) (G,60)

75 75 STL Algorithm – for_each() #include using namespace std; void print(int val){ cout << val << endl; } int main(){ vector x; my_initialization(x); for_each(x.begin(), x.end(), print); return 0; } Output: 22 61...

76 76 STL Algorithms – for_each() template FunctionT for_each(IteratorT first, IteratorT last, FunctionT g){ for(; first != last; ++first) g(*first); return g; }  for_each calls function g() on every element in the container between first and last.

77 Department of Computer Science and Engineering, HKUST 77 Standard Template Library Function Objects

78 78 Function Objects or Functors  STL has a more generalized concept of function pointer: Any “object” can be “called” is a function object or functor.  C function pointers are just one example.  C++ gives more options: any object can be “called” if it supports operator(). class Greater_Than{ private: int limit; public: Greater_Than(int a) : limit(a) {} bool operator()(int value){ return value > limit; } };

79 79 Example - Function Objects class Greater_Than{ private: int limit; public: Greater_Than(int a) : limit(a) {} bool operator()(int value){ return value > limit; } };  Greater_Than is a class.  Greater_Than gt_give(5); creates an object named gt_five of class Greater_Than than is constructed with parameter a=5. This means that, inside gt_give, limit=5.  Now notice that operator() has been overloaded so gt_five(value) is defined to be a function that runs (value > 5).

80 80 Example – Function Objects #include using namespace std; class Greater_Than{ private: int limit; public: Greater_Than(int a) : limit(a) {} bool operator()(int value){ return value > limit; } }; int main(){ Greater_Than gt_five(5), gt_ten(10); if(gt_five(7)) cout 5” << endl; else cout << “7<=10” << endl; return 0; }

81 81 Function Objects Can Carry State class Greater_Than{ private: int limit;// carries state! public: Greater_Than(int a) : limit(a) {} bool operator()(int value){ return value > limit; } };  Function objects can carry state.  e.g., limit = 5 is the state of the gt_five object.

82 82 How to Use Function Objects in STL Algorithms? #include using namespace std; int main(){ Greater_Than g(350); vector x; my_initialization(x); vector ::iterator p; p = find_if(x.begin(), x.end(), g); if(p != x.end()) cout << “Found element” << *p << endl; return 0; }

83 83 How to Use Function Objects in STL Algorithms?  When find_if() examines each item, say x[j] in the container vector x, the Greater_Than function object g will be called using its operator() with the container item, i.e. g(x[j]) // Or formally: g.operator()(x[j])  Since g(i) == 1 if and only if i >= 350 this will find the first item in vector x that is >= 350.

84 84 A Sneakier Way of Writing the Same Thing #include using namespace std; int main(){ vector x; my_initialization(x); vector ::iterator p; p = find_if(x.begin(), x.end(), Greater_Than(350)); if(p != x.end()) cout << “Found element” << *p << endl; return 0; }

85 85 What does this mean? find_if(x.begin(), x.end(), Greater_Than(350));  In this case the Greater_Than(350) is actually a constructor. This is equivalent to writing Greater_Than g(350); and then writing find_if(x.begin(), x.end(), g);  When find_if is running it will call g(x[j]) for the items in vector x.

86 86 Summary of Function Objects  An object that can be called like a function is called a function object or functor (also sometimes functoid), but this often carries other meanings not in STL). Function objects are more powerful than functions, since they can have data members and therefore carry around information or internal states. A function object must have at least the operator() overloaded so that it can be called. A function object (or a function) that returns a boolean value (or type bool ) is called a predicate.

87 87 STL Algorithm – count_if()  Here we count the number of elements that are larger than 10. #include using namespace std; int main(){ vector x; my_initialization(x); int num_count_if(x.begin(), x.end(), Greater_Than(10)); return 0; }

88 88 STL Algorithm – Using for_each() for Summation #include using namespace std; class Sum{ private: int sum; public: Sum() : sum(0) {} void operator()(int value){ sum += value; } int result() const{ return sum; } }; int main(){ list x; my_initialization(x); Sum s = for_each(x.begin(), x.end(), Sum()); cout << “Sum = ” << s.result() << endl; return 0; }

89 89 STL Algorithm – Using for_each() for Summation  In the code for_each(x.begin(), x.end(), Sum()); the Sum() is a constructor that creates an unnamed local object of class Sum.  Pretend this unnamed local function object is called c. Then the for_each runs c(x[j]) for each j. So at the end, c.sum contains the value of the sum of all of the items in x.  But as soon as the statement finishes executing, the local object c goes out of scope and is destructed! So how do we avoid losing the sum we just computed?

90 90 STL Algorithm – Using for_each() for Summation  Recall STL’s template definition of for_each: template FunctionT for_each(IteratorT first, IteratorT last, FunctionT g){ for(; first != last; ++first) g(*first); // return-by-value: returns a copy of the function object return g; }  So the line Sum s = for_each(x.begin(), x.end(), Sum()); calls a copy constructor which makes s into a memberwise copy of c. Thus s.sum becomes the value of the sum of all of the items in x.

91 91 STL Algorithm – Using for_each() for Summation  To confuse matters, beware that the code int main(){ list x; my_initialization(x); Sum s; for_each(x.begin(), x.end(), s); cout << “Sum = ” << s.result() << endl; return 0; } would return 0! The reason that it doesn’t return the expected value is that for_each calls its arguments by value. This means that the total sum is stored in a local copy of s and not in s itself. Therefore s itself never changes from its initially constructed value so s.sum = 0.

92 92 STL Algorithms – transform() Template Iterator2T transform(Iterator1T first, Iterator1T last, Iterator2T result, FunctionT g){ for(; first != last; ++first, ++result) *result = g(*first); return result; }  transform will apply function g() to all the items in the sequence between first and last.  The resulting sequence is written to the location “pointed” to by result.

93 93 STL Algorithm – Using transform() to add // add.h #include using namespace std; class Add{ private: int data; public: Add(int i) : data(i) {} int operator()(int value){ return value + data; } };

94 94 STL Algorithm – Using transform() to add class Print{ private: ostream& os; public: Print(ostream& s) : os(s){} void operator()(int value){ os << value << “ ”; } }; #include “add.h” int main(){ list x; my_initialization(x); vector y(x.size()); transform(x.begin(), x.end(), y.begin(), Add(10)); for_each(y.begin(), y.end(), Print(cout)); cout << endl; }

95 95 STL Algorithm – Using transform() and copy()  The istream_iterator and ostream_iterator templates make it possible to treat streams as sequences. #include #include “add.h” int main(){ list x; my_initialization(x); vector y; transform(x.begin(), x.end(), back_inserter(y), Add(77)); copy(y.begin(), y.end(), ostream_iterator (cout, “\n”)); return 0; }

96 96 Many Other Algorithms in the STL  STL contains many other algorithms, including for example: min_element and max_element equal remove, remove_if (to remove elements) reverse, rotate (to rearrange sequence) random_shuffle binary_search sort (using a function object to compare two elements) merge, unique set_union, set_intersection, set_difference

97 97 STL Documentation  Good documentation for SGI’s extended STL implementation can be found at: http://www.sgi.com/Technology/STL/doc_introduction.html


Download ppt "Department of Computer Science and Engineering, HKUST 1 HKUST Summer Programming Course 2008 Standard Template Library (STL) ~ Generic Programming."

Similar presentations


Ads by Google