a gentle introduction to the standard template library (stl) some references: iburrell/cpp/stl.html...

37
A gentle introduction to the standard template library (STL) Some references: http://www-leland.stanford.edu/~iburrell/cpp/stl.html http://www.cs.rpi.edu/~musser/stl.html http://www.sgi.com/Technology/STL/ (this is so comprehensive that it documents things that aren’t even part of STL yet)

Upload: silvester-hodges

Post on 18-Jan-2016

221 views

Category:

Documents


1 download

TRANSCRIPT

Page 1: A gentle introduction to the standard template library (STL) Some references: iburrell/cpp/stl.html musser/stl.html

A gentle introduction to the standard template library (STL)

Some references:

http://www-leland.stanford.edu/~iburrell/cpp/stl.html

http://www.cs.rpi.edu/~musser/stl.html

http://www.sgi.com/Technology/STL/ (this is so comprehensive that it documents things that aren’t even part of STL yet)

Page 2: A gentle introduction to the standard template library (STL) Some references: iburrell/cpp/stl.html musser/stl.html

STL Organization

containers - hold collections of objects

iterators - iterate through a container’s elements

algorithms - use iterators to implement sort, search, etc.

Page 3: A gentle introduction to the standard template library (STL) Some references: iburrell/cpp/stl.html musser/stl.html

Where the STL code is locatedContainers:#include <list>

#include <vector>

#include <map>

Plus more: deque, queue, stack, set, bitset

Iterators (although you don’t need to include this explicitly to use iterators):

#include <iterator>

Algorithms:#include <algorithm>

Utilities:#include <utility>

#include <functional>

Page 4: A gentle introduction to the standard template library (STL) Some references: iburrell/cpp/stl.html musser/stl.html

STL was adopted in 1994 (fairly recently), so your compiler may not support it yet.

See http://www.cyberport.com/~tangent/programming/stl/compatibility.html

for a list of compatible compilers.

Try the following:

#include <vector>

#include <list>

using namespace std; // may or may not be necessary

...

Page 5: A gentle introduction to the standard template library (STL) Some references: iburrell/cpp/stl.html musser/stl.html

Containers

Sequences:

• basic sequences: vector, list• more exotic sequences: deque, queue, stack, priority_queue

Associative containers:• map (usually a binary tree), multimap, set, multiset

But there are no hash tables (sorry...)

Page 6: A gentle introduction to the standard template library (STL) Some references: iburrell/cpp/stl.html musser/stl.html

vector: usually implemented as a pointer to an array:

• pro: implements fast indexing (looking up the nth element can be done in constant time)

• con: inserting elements into the middle of an array requires copying many elements to make room for the new element

list: usually implemented as doubly linked lists

• pro: easy to insert and remove elements from middle of list (just rearrange pointers, no copying needed)

• con: indexing is slow (finding the nth element requires scanning through the list)

Page 7: A gentle introduction to the standard template library (STL) Some references: iburrell/cpp/stl.html musser/stl.html

common vector, list constructors

vector<float> v1; // create an empty vector of floats

vector<float> v2(10, 5.0); // create a vector that

// initially holds 10 floats,

// each initialized to 5.0

list<float> l; // create an empty list of floats

Page 8: A gentle introduction to the standard template library (STL) Some references: iburrell/cpp/stl.html musser/stl.html

Adding elements to vectors, lists

Elements can be appended to the back of a vector with push_back:

vector<float> v1;

v1.push_back(6.0);

v1.push_back(7.0);

v1.push_back(8.0);

Now v1 contains 3 elements: {6.0, 7.0, 8.0}

Note that the vector is automatically resized so that each new element added with push_back will fit (yay!).

Page 9: A gentle introduction to the standard template library (STL) Some references: iburrell/cpp/stl.html musser/stl.html

The first and last elements of vectors and lists can be retrieved with the front() and back() member functions:

vector<float> v1;

v1.push_back(6.01);

v1.push_back(7.01);

v1.push_back(8.01);

v1.push_back(9.01);

float fFront = v1.front();

float fBack = v1.back();

cout << fFront << " " << fBack << endl;

This prints:

6.01 9.01

Page 10: A gentle introduction to the standard template library (STL) Some references: iburrell/cpp/stl.html musser/stl.html

pop_back() removes the last element from the vector:

vector<float> v1;

v1.push_back(6.01);

v1.push_back(7.01);

v1.push_back(8.01);

v1.push_back(9.01);

v1.pop_back();

cout << v1.front() << " " << v1.back() << endl;

This prints:

6.01 8.01

Page 11: A gentle introduction to the standard template library (STL) Some references: iburrell/cpp/stl.html musser/stl.html

lists support push_front and pop_front (to insert and remove elements at the front of a list), as well as push_back and pop_back:

list<float> l; // create an empty list of floats

l.push_back(6.01);

l.push_back(7.01);

l.push_back(8.01);

l.push_front(9.01);

l.pop_back();

cout << l.front() << " " << l.back() << endl;

This prints:

9.01 7.01

Page 12: A gentle introduction to the standard template library (STL) Some references: iburrell/cpp/stl.html musser/stl.html

vectors support random access with [] and at(). [] performs random access with no bounds checking, while at() performs bounds checking, and throws an out_of_range exception if the index is out of bounds:

vector<float> v2(10, 5.0); // v2 has 10 elements

v2[3] = 5.0;

v2[14] = 6.0; // XXX: out of bounds! memory corruption!

v2.at(14) = 6.0; // out of bounds, throws an exception

cout << v2.at(3) << endl;

Warning: [] and at() do not resize the vector if an index is out of bounds! If you need to increase the size of the vector, use push_back to add elements.

Page 13: A gentle introduction to the standard template library (STL) Some references: iburrell/cpp/stl.html musser/stl.html

Both vectors and lists also support:

size() returns the number of elements in the container

empty() returns true if size()==0, and false otherwise

Page 14: A gentle introduction to the standard template library (STL) Some references: iburrell/cpp/stl.html musser/stl.html

Summary of functions covered so far:

vector list

----------------------------------------

vector() list()

vector(size, init_val)

push_back push_back

push_front

pop_back pop_back

pop_front

front front

back back

[]

at

size size

empty empty

Page 15: A gentle introduction to the standard template library (STL) Some references: iburrell/cpp/stl.html musser/stl.html

So a hypothetical, simplified, definition of vector might look like:

template <class T> class vector {

public:

vector();

vector(int n, const T& val);

void push_back(const T& x);

void pop_back();

T& front();

T& back();

T& operator[] (int n);

T& at(int n);

int size() const;

bool empty() const;

};

A simplified list class would look similar.

Page 16: A gentle introduction to the standard template library (STL) Some references: iburrell/cpp/stl.html musser/stl.html

template <class T> class vector {

public:

vector();

vector(int n, const T& val);

vector(const vector<T>& x);

~vector();

vector<T>& operator= (const vector<T>& x); ...

};

Of course, vector will have a copy constructor, assignment operator, and destructor. The copy constructor and assignment operator for STL container classes make copies of the entire container. This can be expensive, so if you don’t want a copy, you should pass containers by reference:

void foo(vector<float> &v);

rather than by value: void foo(vector<float> v); // copies entire vector

Page 17: A gentle introduction to the standard template library (STL) Some references: iburrell/cpp/stl.html musser/stl.html

Even if you never make copies of entire containers, your elements may be copied as they are inserted into the containers (and at other times).

So if the elements are classes with pointers, they should implement the correct copy constructor, assignment operator and destructor.

Page 18: A gentle introduction to the standard template library (STL) Some references: iburrell/cpp/stl.html musser/stl.html

For classes that shouldn’t be copied (such as large classes or classes with virtual functions), a container of pointers to objects can be used:

vector<Shape*> shapes;

Be aware that the container will not delete the pointed-to objects for you:

vector<Shape*> shapes;

shapes.push_back(new Circle());

shapes.pop_back(); // memory leak: the Circle

// was not deleted!

Correct:vector<Shape*> shapes;

shapes.push_back(new Circle());

delete shapes.back();

shapes.pop_back();

Page 19: A gentle introduction to the standard template library (STL) Some references: iburrell/cpp/stl.html musser/stl.html

For classes that aren’t copiable (such as large classes or classes with virtual functions), a container of pointers to objects can be used:

vector<Shape*> shapes;

Also note that containers of pointers to objects are copied by shallow copy, not deep copy:

vector<Shape*> shapes1;

shapes1.push_back(new Circle());

vector<Shape*> shapes2 = shapes1; // make copy of

// entire vector

Now both shapes1 and shapes2 point to the same Circle object. Be careful not to delete this Circle object twice!

Page 20: A gentle introduction to the standard template library (STL) Some references: iburrell/cpp/stl.html musser/stl.html

STL allows a container’s memory management to be customized with user defined allocators:

template <class T, class A = allocator<T> > class

vector {

public:

typedef typename A::size_type size_type; vector();

vector(size_type n, const T& val);

vector(const vector<T>& x);

~vector();

vector<T>& operator= (const vector<T>& x);

void push_back(const T& x);

void pop_back();

T& front();

T& back();

T& operator[] (size_type n);

T& at(size_type n);

size_type size() const;

bool empty() const;

};

Page 21: A gentle introduction to the standard template library (STL) Some references: iburrell/cpp/stl.html musser/stl.html

template <class T, class A = allocator<T> > class vector {

public:

typedef typename A::size_type size_type;

vector();

vector(size_type n, const T& val);

...

};

For instance, an allocator might be written so that objects are stored in a large, persistent database. In this case, size_type might be larger than a normal int (it might be a 64-bit integer, for instance).

The “= allocator<T>” specifies a default value for vector’s allocator, which is used if you don’t pass in an explicit allocator of your own. You probably will never have to worry about allocators explicitly; chances are you’ll only need the default allocator.

However, if you have an old compiler that doesn’t support default template values, you may have to pass in the default allocator explicitly (yuck):

vector<float, allocator<float> > v1(10, 5.0);

Page 22: A gentle introduction to the standard template library (STL) Some references: iburrell/cpp/stl.html musser/stl.html

Iterators vector list

----------------------------------------

vector() list()

vector(size, init_val)

push_back push_back

push_front

pop_back pop_back

pop_front

front front

back back

[]

at

size size

empty empty

You may have noticed that list had operations front() and back() to read the first and last elements, but how do you get to the rest of the elements?

Page 23: A gentle introduction to the standard template library (STL) Some references: iburrell/cpp/stl.html musser/stl.html

Every container class defines one or more iterators that can be used to scan through the elements of the container:

template <class T, class A = allocator<T> > class list {

public:

typedef typename A::size_type size_type;

typedef ... iterator;

typedef ... const_iterator;

list();

list(const list<T>& x);

~list();

list<T>& operator= (const list<T>& x);

void push_back(const T& x);

void push_front(const T& x);

void pop_back();

void pop_front();

T& front();

T& back();

size_type size() const;

bool empty() const;

};

Page 24: A gentle introduction to the standard template library (STL) Some references: iburrell/cpp/stl.html musser/stl.html

Example:

list<float> l;

l.push_back(6.01);

l.push_back(7.01);

l.push_back(8.01);

l.push_back(9.01);

// Use a list iterator to iterate through l:

list<float>::iterator i;

for(i = l.begin(); i != l.end(); ++i) {

cout << *i << " ";

}

This prints:6.01 7.01 8.01 9.01

Page 25: A gentle introduction to the standard template library (STL) Some references: iburrell/cpp/stl.html musser/stl.html

list<float>::iterator i;

for(i = l.begin(); i != l.end(); ++i) {

cout << *i << " ";

}

list<float>::iterator refers to the iterator defined in the class list:

template <class T, class A = allocator<T> > class list {

public:

typedef typename A::size_type size_type;

typedef ... iterator;

typedef ... const_iterator;

list();

...

};

Page 26: A gentle introduction to the standard template library (STL) Some references: iburrell/cpp/stl.html musser/stl.html

list<float>::iterator i;

for(i = l.begin(); i != l.end(); ++i) {

cout << *i << " ";

}

A list iterator supports several operations:

• The dereference operator (*) returns the element pointed to by the iterator

• ++ advances the iterator to the next element

• != compares the iterator with another iterator to see if the end of the list has been reached

template <class T, class A = allocator<T> > class list {

public:

typedef typename A::size_type size_type;

typedef ... iterator;

typedef ... const_iterator;

list();

...

};

Page 27: A gentle introduction to the standard template library (STL) Some references: iburrell/cpp/stl.html musser/stl.html

list<float>::iterator i;

for(i = l.begin(); i != l.end(); ++i) {

cout << *i << " ";

}

list contains begin() and end() functions which return iterators pointing to the beginning and end of the list:

template <class T, class A = allocator<T> > class list {

public:

typedef typename A::size_type size_type;

typedef ... iterator;

typedef ... const_iterator;

iterator begin();

const_iterator begin() const;

iterator end();

const_iterator end() const; ...

};

Page 28: A gentle introduction to the standard template library (STL) Some references: iburrell/cpp/stl.html musser/stl.html

list<float>::iterator i;

l.push_back(6.0);

l.push_back(7.0);

l.push_back(8.0);

l.push_back(9.0);

for(i = l.begin(); i != l.end(); ++i) {

cout << *i << " ";

}

• begin() returns an iterator which points to the first element of the list

• end() returns an iterator which points one element past the last element in the list. This iterator does not point to a valid element (end() is sometimes used as a “null” iterator to indicate a non-existent element).

6 7 8 9

begin() end()

Page 29: A gentle introduction to the standard template library (STL) Some references: iburrell/cpp/stl.html musser/stl.html

As a second example of begin() and end(), the following prints a list in reverse order:

list<float>::iterator i;

l.push_back(6.0);

l.push_back(7.0);

l.push_back(8.0);

l.push_back(9.0);

for(i = l.end(); i != l.begin(); ) {

--i; // move backwards in the list

cout << *i << " ";

}

This prints:9 8 7 6

6 7 8 9

begin() end()

Page 30: A gentle introduction to the standard template library (STL) Some references: iburrell/cpp/stl.html musser/stl.html

Iterator categories

Not all iterators support the same operations. For instance, suppose we wrote a singly-linked list class:

It would be impractical to iterate backwards through this list, since the links only point forward. So an iterator for singly linked lists might support ++, but not --.

6 7 8 9

begin() end()

Page 31: A gentle introduction to the standard template library (STL) Some references: iburrell/cpp/stl.html musser/stl.html

Iterators can be grouped into 5 categories, each supporting a different set of operations:

• output: ++, * for writing

• input: ++, ==, !=, * and -> for reading

• forward: ++, ==, !=, * and -> for reading and writing

• bidirectional: ++, --, ==, !=, * and -> for reading and writing

• random-access: ++, --, +, -, +=, -=, ==, !=, <, >, <=, >=, * and -> for reading and writing

Examples:

• a singly-linked list would create forward iterators• list (which is a doubly-linked list) creates bidirectional iterators• vector creates random-access iterators

Page 32: A gentle introduction to the standard template library (STL) Some references: iburrell/cpp/stl.html musser/stl.html

These categories form a natural hierarchy:

One might think that based on this hierarchy, iterators would be implemented as classes with appropriate virtual functions and overriding.

However, it was felt that virtual functions would be too inefficient for something as critical as iteration, so iterators were not implemented as classes with virtual functions.

In fact, iterators are not classes (or even types) at all! Instead, each category of iterator is just a set of conventions that various iterator implementations abide by.

input

outputforward bidirectional random-access

Page 33: A gentle introduction to the standard template library (STL) Some references: iburrell/cpp/stl.html musser/stl.html

I said in the last lecture that it was bad style to use a template and to make many assumptions about the type T passed in:

template<class T> // assumes T supports rotate, draw

void rotateAndDraw(T &s, double angle) {

s.rotate(angle);

s.draw();

}

Unfortunately, this is exactly what STL does with iterators. Sometimes this can be confusing, but as long as we understand what is required of the 5 categories of iterators, we can live with it.

Page 34: A gentle introduction to the standard template library (STL) Some references: iburrell/cpp/stl.html musser/stl.html

There are a couple more container operations, insert() and erase(), based on iterators:

vector list

----------------------------------------

vector() list()

vector(size, init_val)

push_back push_back

push_front

pop_back pop_back

pop_front

front front

back back

[]

at

size size

empty empty

begin, end begin, end

insert insert

erase erase

Page 35: A gentle introduction to the standard template library (STL) Some references: iburrell/cpp/stl.html musser/stl.html

• insert adds a new element immediately before an iterator.• erase removes the element pointed to by an iterator

Example:

list<float> l;

l.push_back(6.0);

l.push_back(7.0);

l.push_back(8.0);

l.push_back(9.0);

list<float>::iterator i = l.begin();

i++;

l.insert(i, 17.0);

l.erase(i);

This results in the list {6.0, 17.0, 8.0, 9.0}

Page 36: A gentle introduction to the standard template library (STL) Some references: iburrell/cpp/stl.html musser/stl.html

Warning: the erase operation causes all iterators that pointed to the erased element to be invalidated. So the following is illegal:

list<float>::iterator i = l.begin();

i++;

l.insert(i, 17.0);

l.erase(i);

l.insert(i, 18.0); // illegal! i is invalid here

Page 37: A gentle introduction to the standard template library (STL) Some references: iburrell/cpp/stl.html musser/stl.html

For vectors, any operation that adds or removes elements invalidates all the iterators into the vector. This includes push_back, pop_back, insert, and erase:

vector<float>::iterator i = v.begin();

i++;

v.push_back(); // this invalidates the iterator i

v.insert(i, 17.0); // illegal! i is invalid here

The reason for this is that the vector’s internal array may be copied to a new memory location when the array is resized, meaning that any iterators the pointed into the old array are no longer valid.

Also, remember that insertion and deletion are potentially expensive operations for vectors (though they are cheap operations for lists).