Presentation is loading. Please wait.

Presentation is loading. Please wait.

Elements are always copied when they are put into a container

Similar presentations


Presentation on theme: "Elements are always copied when they are put into a container"— Presentation transcript:

1 Elements are always copied when they are put into a container
Elements are always copied when they are put into a container. So the following is ok: vector<string> v1; { string s1("foo"); v1.push_back(s1); // a copy of s1 goes into v1 } // s1 is deallocated cout << v1.back() << endl; // ok - retrieves copy of s1

2 Elements are returned from containers and iterators by reference:
vector<string> v1; string s1("foo"); v1.push_back(s1); // a copy of s1 goes into v1 string &s2 = v1.back(); // v1.back returns a reference v1.pop_back(); cout << s2 << endl; // s2 references deallocated object In this example, s2 is a reference to an object owned by the container. When the object is popped from the container, s2 no longer references a valid object.

3 To avoid dangling references, you can make a copy of the reference returned by a container or iterator: vector<string> v1; string s1("foo"); v1.push_back(s1); // a copy of s1 goes into v1 string s2 = v1.back(); // copy v1.back() into s2 v1.pop_back(); cout << s2 << endl; // ok now, since s2 was a copy

4 Summary of container insertion and retrieval:
vector<string> v1; string s1("foo"); v1.push_back(s1); // a copy of s1 goes into v1 string s2 = v1.back(); // copy v1.back() into s2 v1.pop_back(); cout << s2 << endl; // ok now, since s2 was a copy Summary of container insertion and retrieval: foo foo user passes reference container makes copy void push_back(T& x) user makes copy container returns reference T& back() foo container

5 Last lecture we covered:
STL sequence containers (list, vector) STL iterators Today we’ll cover: STL associative containers (map, multimap, set, multiset) STL algorithms

6 But first, the pair template
template<class T1, class T2> class pair { public: T1 first; T2 second; pair(T1& v1, T2& v2): first(v1), second(v2) {} }; That’s it. Example: #include <utility> using namespace std; pair<int, float> p1(5, 6.6); cout << p1.first << “ “ << p1.second;

7 Associative containers
The canonical associative container: map (sometimes called a dictionary) A map maps keys to values. For instance, the following map maps keys of type string to values of type double: map<string, double> m1; m1["one"] = 1.0; m1["two"] = 2.0; m1["three"] = 3.0; cout << m1["two"] << endl; This prints: 2

8 (Notice that for strings, “one” < “three” < “two”)
map<string, double> m1; m1["one"] = 1.0; m1["two"] = 2.0; m1["three"] = 3.0; maps support bidirectional iterators. The iterator for a map iterates over (key, value) pairs, in order sorted by the keys: map<string, double>::iterator i; for(i = m1.begin(); i != m1.end(); i++) { pair<string, double> x = *i; cout << x.first << ": " << x.second << endl; } This prints: one: 1 three: 3 two: 2 (Notice that for strings, “one” < “three” < “two”)

9 map operations template<class Key, class T> class map { ...
public: iterator find(const Key& key); T& operator[] (const Key& key); void erase(iterator p); size_type size(); bool empty(); }

10 template<class Key, class T> class map {
... public: iterator find(const Key& key); T& operator[] (const Key& key); void erase(iterator p); size_type size(); bool empty(); } find looks up a key and returns an iterator pointing to the matching (key, value) pair. If the key is not found, find returns the end() iterator. map<string, double>::iterator i; i = m1.find("three"); if(i == m1.end()) {cout << "not found";} else {cout << i->first << ": " << i->second;}

11 template<class Key, class T> class map {
... public: iterator find(const Key& key); T& operator[] (const Key& key); void erase(iterator p); size_type size(); bool empty(); } The [] operator looks up a the value associated with a key, or associates a new value with a key: m1[“three”] = 3.0; cout << m1[“three”] << endl;

12 The [] operator automatically creates a value for a key if that key currently has no value. This is true even when [] is only being used for reading: map<string, double> m1; m1["one"] = 1.0; m1["two"] = 2.0; m1["three"] = 3.0; cout << m1["four"] << endl; This prints: Saying m1[“four”] automatically associates a default value with the key “four” in m1. For primitive types (ints, doubles, etc.), the default is 0. For classes, the default is based on the class’ no-argument constructor. Therefore, maps can only be used with classes that have a no-argument constructor.

13 size returns the number of elements in the map.
template<class Key, class T> class map { ... public: iterator find(const Key& key); T& operator[] (const Key& key); void erase(iterator p); size_type size(); bool empty(); } erase removes an element from a map. Any iterators associated with this element become invalid. However, other iterators remain valid when elements are inserted into or removed from a map. size returns the number of elements in the map. empty returns (size() == 0)

14 Example: the following function prints the frequency of whitespace-separated words in a stream:
void wordFrequency(istream& is) { map<string, int> m; // maps words to their frequency while(is.good() && !is.eof()) { string s; is >> s; // read in the next word m[s]++; // increment the word’s frequency count (this relies on } // the default value being 0) // print the results (the words and their counts): for(map<string, int>::iterator i = m.begin(); i != m.end(); i++) { cout << i->first << ": " << i->second << endl; }

15 Typical implementation of map: balanced binary tree
Lower keys go to the left, higher keys go to the right: map<float, int> m1; m1[1.1] = 10; m1[1.5] = 20; m1[1.7] = 10; m1[2.1] = 20; m1[2.6] = 10; m1[3.3] = 20; m1[4.2] = 10; 2.1: 20 1.5: 20 3.3: 20 1.1: 10 1.7: 10 2.6: 10 4.2: 10

16 where the bool return value indicates whether t1 < t2. Example:
To implement a binary tree, map needs to know how to order the keys. By default, map uses the < operator. We can also pass a pointer to a function to the map constructor to do the comparison (warning: if we do this, things get complicated fast). The function should have type bool (const T &t1, const T &t2) where the bool return value indicates whether t1 < t2. Example: bool stringLess(const string &s1, const string &s2) { return s1 < s2; }

17 So we can declare a map that uses stringLess as follows:
bool stringLess(const string &s1, const string &s2) { return s1 < s2; } map takes a third template argument that specifies the type of the comparison function: template <class Key, class T, class Cmp> map { ... map(Cmp &cmp) {...} So we can declare a map that uses stringLess as follows: map<string, int, bool (*)(const string&, const string&)> m(&stringLess); This is a little on the complicated side.

18 Pointers to functions specify only code (not data)
Pointers to functions specify only code (not data). A language that supported higher-order functions (which contain both code and data) would be more powerful here. C++ can imitate higher-order functions with a surprising hack: C++ allows classes to overload the function call operator: class StringLess { public: // overload function call operator: bool operator() (const string &s1, const string &s2) const { return s1 < s2; } };

19 Now we can use a StringLess object as if it were a function:
class StringLess { public: bool operator() (const string &s1, const string &s2) const { return s1 < s2; } }; Now we can use a StringLess object as if it were a function: StringLess f; bool b = f(“hello”, “goodbye”); // looks like a function call An object with an overloaded function call operator is often called a function object.

20 We can use a function object as an argument to map’s constructor:
class StringLess { public: bool operator() (const string &s1, const string &s2) const { return s1 < s2; } }; template <class Key, class T, class Cmp> map { ... map(Cmp &cmp) {...} We can use a function object as an argument to map’s constructor: map<string, int, StringLess> m(StringLess()); Notice that the type StringLess is passed as the Cmp parameter to the map template, so that map knows what type is being passed to its constructor.

21 And map will instantiate a StringLess object for us.
class StringLess { public: bool operator() (const string &s1, const string &s2) const { return s1 < s2; } }; template <class Key, class T, class Cmp> map { ... map() {...} map(Cmp &cmp) {...} In fact, map has a default constructor that creates a function object based on Cmp’s default constructor. So we can simply say: map<string, int, StringLess> m; // use map’s default constructor And map will instantiate a StringLess object for us.

22 Finally, map also has a default template parameter:
template <class Key, class T, class Cmp = less<Key> > map {...} less is a simple class that uses < to implement the function call operator: template<class T> class less { public: bool operator()(const T &t1, const T &t2) const {return t1 < t2; } }; So this default template parameter allows us to just write: map<string, int> m; // uses less<string> by default

23 So let’s summarize how the simple declaration works:
map<string, int> m1; This declaration of m1: uses the default (no-argument) constructor of map<string,int>. which in turn uses the default template argument map<string,int> (which is less<string>) as a comparison function. map instantiates a less<string> object using less<string>’s default constructor. The less<string> overloaded function invocation operator in turn uses the overloaded < operator to compare strings. This is complicated, but it’s flexible and the defaults are convenient.

24 Various associative containers
multimap maps each key to one or more values map maps each key to exactly one value “Frank” “Frank” “Bob” “Bob” set has keys, but no values multiset allows duplicate keys “Frank” “Frank” “Alice” “Frank” “Bob” “Bob” “Bob”

25 Generic algorithms Now we’ve seen containers and iterators. The final component in the standard template library is algorithms. containers - hold collections of objects iterators - iterate through a container’s elements algorithms - use iterators to implement sort, search, etc.

26 For instance, the sort algorithm works for any random access iterator:
STL algorithms never refer to container classes directly. Instead, the algorithms make use of iterators. Since iterators are standardized into categories, this allows a single algorithm to work for any container that implements the appropriate category of iterator. For instance, the sort algorithm works for any random access iterator: template <class Ran> void sort(Ran first, Ran last); In this declaration, Ran is supposed to by the type of the random-access iterator, and first and last mark the beginning and end of the container to be sorted. input forward bidirectional random-access output

27 template <class Ran> void sort(Ran first, Ran last);
Example: vector<float> v(10, 5.0); ... sort(v.begin(), v.end()); This version of sort uses the < operator to compare element types. Another version of sort takes a comparison function (which computes the less-than relation) as an argument: template <class Ran, class Cmp> void sort(Ran first, Ran last, Cmp cmp); cmp may be a pointer to a function, or it may be an object that overloads the () operator.

28 copy is a simple function that copies elements from one container to another:
vector<float> v1; ... vector<float> v2(v1.size(), 0.0); copy(v1.begin(), v1.end(), v2.begin()); This copies all of v1’s elements into v2. To use copy, the destination container v2 must already have room for the elements that are copied into it. However, a special back_inserter iterator may be used to automatically increase the destination container’s size as elements are added: vector<float> v2; copy(v1.begin(), v1.end(), back_inserter(v2));

29 Surprisingly, STL algorithms can be used with ordinary arrays:
float f[4]; f[0] = 2.3; f[1] = 1.2; f[2] = 4.5; f[3] = 3.4; sort(f, f + 4); This is because pointers into an array are perfectly good random-access iterators, since they define operations like ++, --, +, *, etc.

30 One limitation of basing algorithms on iterators instead of containers is that the algorithms cannot remove elements from a container. However, algorithms can swap elements around (as in sort), and this can be used to simulate removal. For instance, the remove function simply rearranges a container so that all the “removed” elements get moved to the back: 2 3 5 1 5 4 6 7 v1 remove(v1.begin(), v1.end(), 5); 2 3 1 4 6 7 5 5 v1

31 Many algorithms take pointers to functions or function objects as arguments. For instance, for_each takes two input iterators and calls a function for each element in the range specified by the iterators: template<class In, class Op> Op for_each(In first, In Last, Op f) { while(first != last) {f(*first); first++;} return f; } remove_if “removes” all elements (in a range specified by two forward iterators) that satisfy a predicate specified by a pointer to a function or function object: template<class For, class Pred> For remove_if(For first, For last, Pred p);

32 STL provides algorithms for a wide variety of tasks:
nonmodifying sequence operations: for_each, find, count, search, ... modifying sequence operations: transform, copy, generate, rotate, remove, random_shuffle, ... sorted sequence operations: sort, binary_search, ... and many more (60 algorithms in all) STL algorithms promote programming at a higher level - you can program in terms of high-level operations over entire containers, rather than in terms of low-level iterations through containers.


Download ppt "Elements are always copied when they are put into a container"

Similar presentations


Ads by Google