270 likes | 581 Views
Standard Template Library (STL) Containers. Moshe Fresko Bar-Ilan University Object Oriented Programming 2007-2008. Standard Library Design. The C++ Standard Library Provides support for language features such as memory management and run-time type information
E N D
Standard Template Library (STL) Containers Moshe Fresko Bar-Ilan University Object Oriented Programming 2007-2008
Standard Library Design The C++ Standard Library • Provides support for language features such as memory management and run-time type information • Supplies information about implementation-defined aspects of the language, such as the largest float value • Supplies functions that cannot be implemented optimally in the language itself for every system, such as sqrt() and memmove() • Supplies non-primitive facilities that a programmer can rely on for portability, such as lists, maps, sort functions, and I/O streams • Provides a framework for extending the facilities • Provides the common foundation for other libraries
Containers • Container is an object that holds other objects • Examples: lists, vectors, and associative arrays • Objects can be added and removed from a container • STL defines two kinds of containers • Sequences • Associative Containers
Containers <vector> one-dimensional array of T <list> doubly-linked list of T <deque> double-ended queue of T <queue> queue of T (priority_queue is defined here) <stack> stack of T <map> associative array from T to Value (multimap is defined here) <set> set of T (multiset is defined here) <bitset> array of booleans
vector • Vectors are dynamic containers that keep sequence of elements of a template type T. • The standard vector is a template defined in namespace std and presented in <vector>. • It first defines a set of types template <class T, class A=allocator<T> > class std::vector { public: typedef T value_type; // type of element typedef A allocator_type; // type of mem.mngr. typedef typename A::size_type size_type; typedef typename A::difference_type difference_type; typedef ……… iterator; // T* typedef ……… const_iterator; // const T* typedef std::reverse_iterator<iterator> reverse_iterator; typedef std::reverse_iterator<const_iterator> const_reverse_iterator; typedef typename A::pointer pointer; typedef typename A::const_pointer const_pointer; typedef typename A::reference reference; typedef typename A::const_reference const_reference; //……… };
vector types usage Example template<class C> typename C::value_type sum(const C& c) { typename C::value_type s=0; typename C::const_iterator p=c.begin(); while (p!=c.end()) { s+=*p; ++p; } return s; } // Usage of that function int main() { vector<int>& vi; vector<float>& vf; vector<Complex>& vc; // ......... int isum = sum(vi); float fsum = sum(vf); Complex csum = sum(vc); }
iterators • Iterators are used to navigate containers without the programmers having to know the actual type used to identify the elements. template <class T, class A=allocator<T> > class std::vector { public: // ……… iterator begin(); // points to 1st element const_iterator begin() const; iterator end(); // points to one-past-last element const_iterator end() const; reverse_iterator rbegin(); // 1st element of reverse seq. const_reverse_iterator rbegin() const; reverse_iterator rend(); // one-past-last element of reverse seq. const_reverse_iterator rend() const; // ……… }; • begin()/end() works in ordinary element order • rbegin()/rend() works in reverse element order • In a list of three elements A, B, C begin() end() A B C rend() rbegin()
iterators • To keep track of a list of Complex numbers … vector<Complex> vec ; vec.push_back(Complex(3,4)) ; vec.push_back(Complex(5.0,6.6)) ; vec.push_back(Complex(-3.1,5.2)) ; … vector<Complex>::iterator it; for(it=vec.begin();it!=vec.end();++it) { Complex c = *it ; cout<<“The next Complex number in list is:”<<c<<endl; } for(vector<Complex>::reverse_iterator rit=vec.rbegin(); rit!=rend();++rit) cout<<“Reverse order next Complex number:”<<*rit<<endl; …
iterators • Example: A utility that searches for the last occurrence of an element in a sequence template<class C> typename C::iterator find_last(C& c, typename C::value_type v) { typename C::reverse_iterator p=c.rbegin(); while (p!=c.rend()) { if (*p==v) { typename C::iterator i=p.base(); return --i; } ++p; } return c.end(); }
Element Access • In vector container types one can easily and efficiently access individual elements in any order. template <class T, class A=allocator<T> > class std::vector { public: // ……… reference operator[](size_type n); // unchecked access const_reference operator[](size_type n) const; reference at(size_type n); // checked access const_reference at(size_type n) const; reference front(); // first element const_reference front() const; reference back(); // last element const_reference back() const; // ……… };
Element Access • operator[]() provides unchecked access whereas at() does a range check and may throw out_of_range.void f(vector<int>& vi, int i1, int i2) { for (int i=0;i<vi.size();++i) { int next=v[i]; // … } try { v.at(i1)=v.at(i2); // range check is done } catch(out_of_range) { // … } } • The return values are reference or const_reference depending on the container object being const or not. • For vector<X>, reference is simply X& and const_reference is const X&. vector<int> vi(1); vi[0]=10; int& i=vi[0]; i=20; // same as v[i]=20 • The effect of trying to create an out_of_range reference is undefined. • front() returns the element pointed by begin().
Constructors, Destructors, Copy operators template <class T, class A=allocator<T> > class std::vector { public: // ……… explicit vector(const A& =A()); explicit vector(size_type n, const T& val=T(), const A&=A()); template <class In> // In must be an input iterator vector(In first,In last,const A&=A());// copy from [first:last[ vector(const vector& x); ~vector(); vector& operator=(const vector& x); template <class In> void assign(In first, In last);// In must be an input iterator void assign(size_type n, const T& val); // ……… };
Examples for Ctor, Dtor vector<Record> vr(10000); void f(int s1, int s2) { vector<int> vi(s1); vector<double>* p=new vector<double>(s2); } class Num { public: Num(long); } vector<Num> v1(1000); // Error. No default ctor vector<Num> v2(1000,Num(0)); // ok void f(const list<X>& lst) { vector<X> vl(lst.begin(),lst.end()); char p[]=“Something”; vector<char> v2(p,&p[sizeof(p)-1]); }
Stack operations template <class T, class A=allocator<T> > class std::vector { public: // ……… void push_back(const T& x); // add to end void pop_back(); // remove last element // ……… }; • Example void f(vector<char>& s) { s.push_back(‘a’); s.push_back(‘b’); s.push_back(‘c’); s.pop_back(); // s.back()==‘b’ s.pop_back(); // s[s.size()-1]==‘a’ }
List Operations • To add/remove elements to/from the middle of the vector template <class T, class A=allocator<T> > class std::vector { public: // ……… iterator insert(iterator pos, const T& x); void insert(iterator pos,size_type n, const T& ); template <class In> void insert(iterator pos, In first, In last); iterator erase(iterator pos); iterator erase(iterator first, iterator last); // ……… };
Example List Operations • Examples vector<string> fruit; fruit.push_back(“peach”); fruit.push_back(“apple”); fruit.push_back(“kiwi”); fruit.push_back(“pear”); fruit.push_back(“starfruit”); fruit.push_back(“grape”); sort(fruit.begin(),fruit.end()); vector<string>::iterator p1=find_if(fruit.begin(),fruit.end(),initial(‘p’)); vector<string>::iterator p2=find_if(p1,fruit.end(),initial_not(‘p’)); fruit.erase(p1,p2); // deletes those starting with ‘p’ fruit.erase(find(fruit.begin(),fruit.end(),”starfruit”)); fruit.erase(fruit.begin()+1); fruit.insert(fruit.begin()+1,”cherry”); fruit.insert(fruit.end(),”orange”);
Size and Capacity • A vector grows as needed and sometimes it is worthwhile to affect its growth. template <class T, class A=allocator<T> > class std::vector { public: // ……… size_type size() const(); // number of elements bool empty() const {return size()==0;} size_type max_size() const; // size of largest vec void resize(size_type sz, T val=T()); size_type capacity() const;// size of mem void reserve(size_type n); // reserve mem // ……… };
Size and Capacity - Example class Histogram { vector<int> count; public: Histogram(int h) : count(max(h,8)) { } void record(int i) { if (i<0) i=0; if (count.size()<=i) count.resize(i+1); count[i]++; } }; void addNNumbers(vector<int>& vi, int n) { vi.clear(); vi.reserve(n); for (int i=0;i<n;++i) vi.push_back(i); }
Other member functions template <class T, class A=allocator<T> > class std::vector { public: // ……… void swap(vector&); allocator_type get_allocator()const; // ……… }; template <class T, class A> bool std::operator==(const vector<T,A>& x, const vector<T,A>& y); bool std::operator<(const vector<T,A>& x, const vector<T,A>& y); // Example: for giving memory back to system vector<X> v; //… // many operations on v // { vector<X> tmp=v; v.swap(tmp); }
Exercises • Create a vector containing the letters of alphabet in order. Print them in reverse order. • Create a string vector. Read from the user strings and insert to it. Sort and print the sorted list. • Create a two dimensional string vector. Read names from the user and insert it into a vector according to its initial character. ‘A’ will be inserted into the 65th vector, ‘B’ into 66th, etc. • Write a class Student containing name, idnumber, and grade. Create a vector of students and fill it. Sort them. How can you find a student and change his grade?
List • List keeps a sequence of elements like vector. But it is optimized for insertion and deletion. • It is implemented by doubly-linked list. template <class T, class A=allocator<T> class std::list { public: // types and operations like vector // except [], at(), capacity(), reserve() // … };
Splice, sort, merge • List has some operations suited for doubly linked lists. template <class T, class A=allocator<T> class std::list { public: void splice(iterator pos, list&x); // move all elements from x to pos void splice(iterator pos, list& x, iterator p); // move only *p from x void splice(iterator pos, list& x, iterator first, iterator last); void merge(list&); template <class Cmp> void merge(list&,Cmp); void sort(); template <class Cmp> void sort(Cmp); // … };
Comparisons • Associative containers require that the key elements can be ordered. • Some container operations (like sort, merge, binary_search) require ordering. • Default ordering is done according to operator<. • If an ordering criteria is cmp • cmp(x,x) is false • If cmp(x,y) and cmp(y,z) then cmp(x,z) • Define equiv(x,y) as !(cmp(x,y)||cmp(y,x)If equiv(x,y) and equiv(y,z) then equiv(x,z)
Comparisons - example • Consider template <class R> void sort(R first, R last); template <class R, class Cmp> void sort(R first, R last, Cmp cmp); • For sorting a string list case-insensitive class NoCase { public: bool operator()(const string&x,const string&y) const { string::const_iterator p=x.begin(),q=y.begin(); while (p!=x.end() && q!=y.end() && toupper(*p)==toupper(*q)) { ++p; ++q; } if (p==x.end()) return q!=y.end(); return toupper(*p)<toupper(*q); } }; • Usage sort(fruit.begin(),fruit.end(),NoCase());