Standard Template Library

- Programming often involves using a common set of techniques and methods multiple times

- For example, programmers working on different projects might all need to use linked lists

- Each programmer needing a linked list could design and develop his/her own custom linked list class

- Although this would be feasible, it would be a tremendous amount of duplication of effort

- Rather than “re-inventing the wheel”, it would be more effective to develop the class one time

- The class could then be shared and used by multiple programmers all requiring the same functionality

- Similar classes describing common methods could also be developed and stored together in a library

- This was the motivation and goal for the development of the Standard Template Library (STL)

- Included in the STL is a collection of C++ classes encapsulating common programming techniques
- The STL provides elegant solutions to managing collections of data with modern and efficient algorithms
- STL components are implemented as abstract types using templates for maximum flexibility

- By using the STL, programmers leverage expertise of others and avoid duplicating code
- The STL is very large; we'll look at some of the more commonly used components

- We’ll focus our discussion on the following STL elements

- Containers (vector, deque, and list)

- Iterators

- Algorithms


 

 

Containers
- Containers are template classes in the STL that manage a collection of elements

- Containers encapsulate behaviors for a wide variety of different data structures

- During our study of templates, recall that we designed an abstract stack class

- The class defined the behavior of a stack data structure with parameterized type (using templates)

- This is similar to how STL container classes are designed and implemented

- Different types of containers perform different programmatic data structure needs

- Containers are grouped into two main categories

 

Sequence containers
- Collection of elements are ordered by position in the list (place of insertion)
- Position in list is independent of the value of the element
- Sequence container types include: vector, list, deque

Associative containers
- Collection of elements are ordered and sorted by the value of the element
- Order is independent of the position in the list (place of insertion)
- Associate container types include: set, multiset, map, multimap

 

vector
-
To demonstrate, let’s look at the STL vector sequence container

- This container manages elements as a dynamic array (size not known until run-time)

- Programmers using container classes do not need to manage this dynamic memory

- All memory management is done internally by the class
- Elements can easily be added and removed from the end (back) of the list quickly
- Insertion in middle and front requires more time - all elements need to be moved
- Can randomly access elements directly in a manner similar to arrays

- The vector container makes it easy to manage unknown amounts of elements

- Consider the example below

 

#include <iostream>

#include <vector>       // vector header file

 

using namespace std;

 

main()

{

  int i;                        // loop counter

  vector<int> iv;               // vector container of integers

 

  // Load container elements with values

  for( i = 10; i < 16; ++i )

    iv.push_back( i );

 

  // Print container elements

  for( i = 0; i < iv.size(); ++i )

    cout << "iv[" << i << "]: " << iv[i] << endl;

}

Output
iv[0]: 10
iv[1]: 11
iv[2]: 12
iv[3]: 13
iv[4]: 14
iv[5]: 15
 

- Before an STL class is used, we must include its class declaration

 

#include <vector>

 

- An empty “vector of integers” named iv created using template syntax

 

vector<int> iv;

- Memory allocation of elements handled by vector class as elements are added

- Elements are added (pushed) to the back of the list

 

iv.push_back( i );


- This same method is used in many of the sequence container classes
- The size method returns the current number of elements in container

 

for( i = 0; i < iv.size(); ++i )


- Note how elements can be accessed randomly through array-like indexing

- Finally, note how the output stream operator is overloaded in the class

 

cout << "iv[" << i << "]: " << iv[i] << endl;
 
 

 

 

deque

- STL containers all operate and are used in very similar manners

- Consider another example of an STL container class called a deque

- Pronounced "deck", this class is designed as a "double-ended queue"
- This class also manages elements in a dynamic array (size not known until run-time)
- Can add and remove elements from both ends (front, back) quickly
- Insertion in middle requires more time - all elements need to be moved
- Can randomly access elements directly

 

#include <iostream>

#include <deque>        // deque header file

 

using namespace std;

 

main()

{

  int i;                        // loop counter

  deque<int> id;                // deque container of integers

 

  // Load container elements with values at front

  for( i = 10; i < 16; ++i )

    id.push_front( i );

 

  // Load container elements with values at end

  for( i = 20; i < 26; ++i )

    id.push_back( i );

 

 

  // Print container elements

  for( i = 0; i < id.size(); ++i )

    cout << "id[" << i << "]: " << id[i] << endl;

}

Output
id[0]: 15
id[1]: 14
id[2]: 13
id[3]: 12
id[4]: 11
id[5]: 10
id[6]: 20
id[7]: 21
id[8]: 22
id[9]: 23
id[10]: 24
id[11]: 25

- Memory allocation of container elements handled by deque class
- Created initially as an empty deque of integers

 

deque<int> id;


- Note the method used to append (push) elements to front of container (not provided in vector)

 

id.push_front( i );


- Note method to append (push) elements to end of container

 

id.push_back( i );


- Note size method returns number of elements in container

 

for( i = 0; i < id.size(); ++i )


- Note indexing of elements with subscript operator and overloaded output stream

 

 cout << "id[" << i << "]: " << id[i] << endl;


 


 

list
- STL container implementing behavior of a linked list

- Contains references to the next and previous element in the list (doubly linked list)
- Manages elements in a dynamic array (size not known until run-time)
- Can add and remove elements anywhere (front, middle, back) quickly
- Cannot randomly access elements directly (must navigate through the chain)

 

#include <iostream>

#include <list>         // linked list header file

 

using namespace std;

 

main()

{

  int i,j;

  list<int> il;                 // linked list container of integers

 

  // Load container elements with values at front

  for( i = 10; i < 16; ++i )

    il.push_front( i );

 

  // Load container elements with values at end

  for( i = 20; i < 26; ++i )

    il.push_back( i );

 

 

  // Print container elements

  while( !il.empty() )

    {

      cout << il.front() << endl;

      il.pop_front();

    }

}

Output
15
14
13
12
11
10
20
21
22
23
24
25
 

- Memory allocation of container elements handled by list class
- Created, initially, as an empty list of integers

 

list<int> il;             


- Note methods to append (push) elements to front and end of list container
- Note method to append (push) elements to end of container

 

il.push_front( i );

il.push_back( i );


- Note method to return false if no elements in container

 

il.empty()


- Note method to return first element in the list

 

il.front()


- Note method to remove (pop) first element in the list

 

il.pop_front();
 


 

More on Containers
- All containers have several common operations implemented as methods
- Some of these common methods include:

 

size()
Returns the actual number of elements in the container

empty()
Returns whether the container is empty or not

swap()
Swaps data with another container

insert()
Inserts elements into the container

erase()
Removes elements from the container

clear()
Removes all elements making it empty

- Some containers add their own specific methods to the common set
- The list container, for example, adds the following types of methods:

 

splice()
Inserts elements from one container into another

sort()
Sorts elements in the list

unique()
Removes duplicate elements in the list
 
 

 

Iterators
- The STL includes special class objects to "iterate" or navigate over elements in a list
- Similar to pointers, iterators "point to" elements in a list
- Iterators represent positions of elements within a list
- Iterators return elements at certain positions in a list
- Each container class defines its own iterator as a nested class within the container
- Each container defines two types of iterators:

 

container type::iterator
can iterate, access, and change the elements referenced by the iterator

container type::const_iterator
can only iterate and access the elements referenced by the iterator

- Container classes define member methods to use the iterators for accessing elements
- Example of such methods include:

 

begin()
Returns an iterator that represents the beginning of the elements in the list

end()
Returns an iterator that represents the position behind the last element
Can only be used in an equality or comparison to test end of list

++ operator
Operator to step to the next element "pointed" to by the iterator

* operator
Operator to return the element "pointed" to by the iterator

- Consider an example that uses an iterator to access elements in a linked list

- Since list elements cannot be accessed randomly, iterators can be used instead

 

#include <iostream>

#include <list>

 

using namespace std;

 

main()

{

  int i,j;

  list<int> il;    // linked list container of integers

 

  // Load container elements with values at front

  for( i = 10; i < 16; ++i )

    il.push_front( i );

 

 

  // Declare the iterator

  list<int>::const_iterator pos;

 

  // Assign the iterator to beginning of list;

  //   step through the list with the iterator

  //   print the element referenced by the iterator

  for( pos = il.begin(); pos != il.end(); ++pos )

    cout << *pos << endl;


 

- A constant linked list iterator is declared to access list elements

 

list<int>::const_iterator pos;

 

- Note how the begin(), end(), and ++ operator methods return iterators

- The iterator stores references to the elements as it loops through the list

 

for( pos = il.begin(); pos != il.end(); ++pos )

 

- The * operator is then used to access the actual value of the element in the list

 

cout << *pos << endl;

 

 

 

 

Algorithms

- Another very useful feature of the STL is the inclusion of robust algorithms

- These algorithms process elements in collections
- Algorithms are NOT member functions of container classes
- Rather, they are implemented as global functions that operate on iterators
- Types of algorithms include searching, sorting, copying, reordering
- The header file <algorithm>  must be included to use the algorithms
- To demonstrate algorithms, let’s put everything we’ve learned about the STL together

- The example below shows the combined use of containers, iterators, and algorithms

#include <iostream>

#include <list>

#include <algorithm>

 

using namespace std;

 

void printList( list<int> &l )

{

  list<int>::const_iterator pos;

  cout << "List:" << endl;

  for( pos = l.begin(); pos != l.end(); ++pos )

    cout << *pos << endl;

}

 

main()

{

  int i;

  list<int> il;                 // linked list container of integers

 

  // Load container elements with values at front

  for( i = 10; i < 16; ++i )

    il.push_front( i );

  printList( il );

 

  // Sort the list (using list member method)

  il.sort();

  printList( il );

 

  // Use STL algorithm find to find an element with

  // value 12 and return an iterator to this position

  list<int>::iterator fpos;

  fpos = find( il.begin(), il.end(), 12 );

 

  // Insert an element at the position found

  il.insert( fpos, 101 );

  printList( il );

 

  // Reverse order of the list using STL algorithm

  reverse( il.begin(), il.end() );

  printList( il );

Output
List:
15
14
13
12
11
10
List:
10
11
12
13
14
15
List:
10
11
101
12
13
14
15
List:
15
14
13
12
101
11
10

 

    STL and User classes
     
- We can use our own classes with STL container classes
     
- Consider the example below

#include <iostream>

#include <list>

 

using namespace std;

 

class Test

{

  friend ostream &operator<<( ostream &strm, const Test &t )

  {

    strm << "Test:" << endl;

    strm << "  val: " << t.val << endl;

    return strm;

  }

 

private:

  int val;

 

public:

  Test( int v=0.0 ) { val = v; }

 

};

 

main()

{

  int i;

  list<Test> tl;

 

  // Load container elements with values at front

  for( i = 10; i < 16; ++i )

    tl.push_front( Test(i) );

 

  // Print container elements

  list<Test>::const_iterator pos;

  for( pos = tl.begin(); pos != tl.end(); ++pos )

    cout << *pos << endl;

}

 

Output

Test:

  val: 15

 

Test:

  val: 14

 

Test:

  val: 13

 

Test:

  val: 12

 

Test:

  val: 11

 

Test:

  val: 10

 

 

      - We can add objects of our own classes to STL containers

 

tl.push_front( Test(i) );

 
      
- The iterator references an object and prints the state using our overloaded stream operator

 

cout << *pos << endl;

 

      - As we’ve seen the STL can be a tremendous resource for C++ programmers

      - Rather than designing our own data structure classes, we can now utilize STL classes

      - The STL can assist us with its solutions to some of the more common programming problems