The Standard Template Library (STL)
The standard template library (STL) is a set of template classes and functions that supply the programmer with common data structures and functions such as lists, stacks, arrays, sorting, etc utilised through templates.
The STL is comprised of the following components:
- Containers for storing information.
- Iterators for accessing the information stored.
- Algorithms for manipulating the content of the containers.
Containers
Containers are STL classes that are used to store data. Each container class defines a set of functions that may be applied to the container. For example, a list container includes functions that insert, delete, and merge elements. A stack includes functions that push and pop values.
STL supplies provide the following container classes:
- Sequential containers
- Associative containers
- Container Adapters
Sequential Containers
These are ordered collections used to hold data sequentially. Each element holds a position that depends on the time and place of insertion. Adding any object to the end of a vector means that the object will appear in the order that was inserted. Sequential containers are characterised by a fast insertion time but are relatively slow in find operations.
The STL provides five sequence container classes:
std::vector – The vector class implements a dynamic array and thus can grow as needed. Although a vector is dynamic, it still uses the standard array subscript notation to access its elements. Compared to the other dynamic sequence containers, vectors are very efficient at accessing elements and relatively efficient in adding or removing elements from their end. For operations that involve inserting or removing elements at positions other than the end, they perform worse than the other sequence container classes
std::deque – Similar to std::vector except that it allows for new elements to be inserted or removed at the beginning. Unlike vectors, deques are not guaranteed to store all elements in contiguous storage locations. Attempting to access elements by offsetting a pointer to another element will result in undefined behaviour. For operations that involve frequent insertion or removals of elements at positions other than the beginning or the end, deques are less efficient than lists and forward lists.
std::list – These are sequence containers that allow constant time insert and erase operations anywhere within the sequence, and iteration in both directions. Compared to other standard sequence containers, lists generally perform better in inserting, extracting, and moving elements in any position within the container. The main drawback of lists and forward_lists compared to these other sequence containers is that they lack direct access to the elements by their position and searching can take extra time. They also consume some extra memory to keep the linking information associated with each element.
std::forward_list – Similar to a std::list except that it is a singly linked list of elements that allows iteration only in one direction. Compared to other base standard sequence containers forward_list generally performs better in inserting, extracting, and moving elements in any position within the container. The main drawback of forward_lists is that like the standard list, they lack direct access to the elements by their position and consume extra resources to keep the linking information current.
std::array class – This is a templated class that provides many benefits over built-in arrays, such as maintaining the array size preventing automatic decay into a pointer, providing bounds checking, and allowing the use of C++ container operations.
Associative Containers
Associative containers utilise sorted data structures that can be quickly searched using keys. This results in slower insertion times but offers significant advantages when searching.
The associative containers supplied by STL are –
std::set
std::unordered_set
std::map
std::unordered_map
std::multiset
std::unordered_multiset
std::multimap
std::unordered_multimap
Container Adapters
Container Adapters offer limited functionality oriented toward a particular container class and provide a more relevant set of functionality. The main adapter classes are:
std::stack — Stores elements in a LIFO (last-in-first-out) fashion, allowing elements to be inserted (pushed) and removed (popped) at the top.
std::queue — Stores elements in FIFO (first-in-first-out) fashion, allowing the first element to be removed in the order they’re inserted.
std::priority_queue — Stores elements in sorted order, such that the one whose value is evaluated to be the highest is always first in the queue.
Iterators
Iterators are template classes that act like pointers. They provide the means to cycle and manipulate the contents of a container in much the same way that you would use a pointer to cycle through an array. There are five types of iterators:
Bidirectional iterator - Store and retrieve values. Moves forward and backward.
Forward iterator - Store and retrieve values. Forward moving only.
Input iterator - Retrieve, but not store values. Forward moving only.
Output iterator - Store, but not retrieve values. Forward moving only.
Random access iterator - Store and retrieve values. Elements may be accessed randomly.
STL Algorithms
Algorithms act on containers. Each container supports its basic operations however the standard algorithms header provides a more extensive range of functionality. One algorithm function can be used on any container type. Algorithms from STL are tried and tested saving time & effort when manipulating data. The algorithm library covers sorting, searching, modifying, non-modifying, and Minimum and Maximum operations.
Vector examples
The following code sample uses vectors to create and manipulate an int vector
#include <iostream> #include <vector> #include <algorithm> using namespace std; int main() { vector <int> v; // create zero-length vector unsigned int i; cout << "initial size of vector v = " << v.size() << endl;// display original size of v for(i=0; i<20; i++) v.push_back(i); cout << "new size of vector v = " << v.size() << endl; // display new size of vector v cout << "Current contents of vector v:\n"; for(i=0; i<v.size(); i++) cout << v[i] << " ";// display contents of vector v cout << endl; cout << "add 5 elements to vector v:\n"; for(i=0; i<5; i++) v.push_back(i+10);//add 5 more values to vector cout << "new size of vector = " << v.size() << endl;// display current size of v cout << "Current contents of vector v:\n"; for(i=0; i<v.size(); i++) cout << v[i] << " ";// display contents of vector v cout << endl; sort(v.begin(), v.end());//sort contents of vector v cout << "Sort contents of vector v and list\n"; for(i=0; i<v.size(); i++) cout << v[i] << " "; // display contents of sorted vector cout << endl; vector <int> v1(10,5);//create new int vector v1 and initialise elements to 5 cout << "create new int vector v1. Initialise elements to 5. Merge with vector v\n"; for(i=0; i < v1.size();i++) v.push_back(v1[i]);// merged vector v and v1 for(i=0; i < v.size(); i++) cout << v[i] << " ";//display contents of v1 cout << endl; cout << "Clear contents of vector v\n"; v.clear();// clear contents of vector cout << "new size of vector = " << v.size() << endl; return 0; }
The following code sample uses template lists to create and manipulate an int vector
#include <iostream> #include <list> using namespace std; int main() { list <int> lst;// create an empty list lst list <int> lst1; // create an empty list lst1 cout << "create random integer lists: " << endl; int rn; for(int a=10;a>0;a=a-1) { lst.push_back(rand()); //add random number to lst lst.push_back(rand()); //add random number to lst1 } list<int>::iterator p = lst.begin();//create pointer p and set to start of lst cout << "output contents of lst: " << endl; while(p != lst.end()) { //iterate through entire lst cout << *p << " "; //output lst value cout << endl; p++; } lst.sort(); cout << "sort lst: " << endl; p = lst.begin(); while(p != lst.end()) { cout << *p << " ";//output lst element cout << endl; p++; } cout << "merge and sort" << endl; lst.merge(lst1);//merge lst and lst lst.sort();//sort lst p = lst.begin(); while(p != lst.end()) { cout << *p << " ";//output lst cout << endl; p++; } }