C++ Intro

Lecture 7: C++ Standard Library, the STL


Standard Template Library


Our overview of C++ Standard Library, STL for short, covers its containers, iterators, and algorithms. Containers and iterators are C++ objects; the algorithms are functions. All STL components are C++ templates. That's where the abbreviation STL takes its origin - the Standard Template Library.

An animation of STL components helps to visualize how objects interact at runtime.

In C++ there are two kinds of templates: C++ template classes, such as std::vector, and C++ template functions, such as swap, sort, and so on. The idea of a template is to parameterize part of the program by types of variables. This is called generic programming, which presumes that a program will work correctly with any type. The typenames designated as template parameters themselves become 'variables' at compile time.

While creating templates is one of the most challenging programming tasks in C++, using prefabricated templates from the STL library is quite easy:


#include <vector>
#include <string>
using namespace std;
int main (int argc, char* argv[])
{
    vector< double > vd; // vd elements are floating point numbers
    vector< int > vi;    // vi elements are integer numbers
    vector< string > vs; // vs elements are string objects
    return 0;
}

The typenames which appear inside angled brackets are template parameters. Template function calls normally ommit template parameters, because the typenames of their template parameters are absorbed directly from the actual function parameters. However, template parameters of functions may also be specified by the programmer:


#include <cassert>
#include <algorithm>
using namespace std;
int main (int argc, char* argv[])
{
    int one = 1;
    int two = 2;
    swap< int >( one, two ); // explicit template param
    swap( one, two );        // implicit template param
    assert( one == 1 );
    assert( two == 2 );
    return 0;
}

Note that because swap expects both arguments to be of the same type, it needs just one template parameter.

While STL comes with every C++ compiler, the language permits development of the third-party libraries. Many more libraries exist besides STL. Free libraries can be found through various web directories. For example, Google has its own directory of C++ Class Libraries.


STL Containers


Containers replicate structures commonly used in programming:


std::stack


A stack is a container that permits to insert and extract its elements only from the top of the container:


#include <cassert>
#include <stack>
using namespace std;
int main (int argc, char* argv[])
{
    stack< int > st;
    st.push( 100 );           // push number on the stack
    assert( st.size() == 1 ); // verify one element is on the stack
    assert( st.top() == 100 );// verify element value
    st.top() = 456;           // assign new value
    assert( st.top() == 456 );
    st.pop();                 // remove element
    assert( st.empty() == true );
    return 0;
}


std::set


A set is a container that holds unique elements. The elements in std::set are always sorted.


#include <cassert>
#include <iostream>
#include <set>
using namespace std;
int main (int argc, char* argv[])
{
    set< int > iset;   // set of unique integer numbers

    iset.insert( 11 ); // populate set with some values
    iset.insert( -11 );
    iset.insert( 55 );
    iset.insert( 22 );
    iset.insert( 22 );
    if ( iset.find( 55 ) != iset.end() ) { // is value already stored?
        iset.insert( 55 );
    }
    assert( iset.size() == 4 ); // sanity check :-)
    set< int >::iterator it;
    for ( it = iset.begin(); it != iset.end(); it++ ) {
        cout << " " << *it;
    }
    return 0;
}
// Output:  -11 11 22 55


std::map


A map is a container that holds unique pairs of keys and values. The elements in std::map are always sorted by its keys.

Each element of the map is formed by the combination of the key value and a mapped value.

Map iterators access both the key and the mapped value at the same time.


#include <cassert>
#include <iostream>
#include <string>
#include <map>
using namespace std;
int main (int argc, char* argv[])
{
    map< string, string > phone_book;
    phone_book[ "411" ] = "Directory";
    phone_book[ "911" ] = "Emergency";
    phone_book[ "508-678-2811" ] = "BCC";
    if ( phone_book.find( "411" ) != phone_book.end() ) {
        phone_book.insert(
            make_pair(
                string( "411" ),
                string( "Directory" )
            )
        );
    }
    assert( phone_book.size() == 3 );
    map< string, string >::iterator it;
    for ( it = phone_book.begin(); it != phone_book.end(); ++it ) {
        cout
            << " " << it->first
            << " " << it->second
            << endl
            ;
    }
    return 0;
}
/* Output:
 411 Directory
 508-678-2811 BCC
 911 Emergency
*/


std::pair


An std::pair< T1, T2 > is a C++ structure that holds one object of type T1 and one of type T2. A pair is much like a container that holds exactly two elements. The pair is defined in the standard header named utility:


#include <cassert>
#include <string>
#include <utility>
using namespace std;
int main (int argc, char* argv[])
{
    pair< string, string > strstr;
    strstr.first = "Hello";
    strstr.second = "World";

    pair< int, string > intstr;
    intstr.first = 1;
    intstr.second = "one";

    pair< string, int > strint( "two", 2 );
    assert( strint.first == "two" );
    assert( strint.second == 2 );

    return 0;
}

Functions that need to return two values often return a pair:


pair< bool, double > result = do_a_calculation();
if ( result.first )
  do_something_more( result.second );
else
  report_error();



Iterators


An iterator is an object that provides access to objects stored in STL containers. The entire concept of C++ pointer is crucial to understanding general semantics of iterators, because iterators are designed to behave like C++ pointers. For example,


#include <iostream>
#include <vector>
using namespace std;
int main (int argc, char* argv[])
{
    vector< int > vint( 3 ); // vector with 3 integer numbers
    vint[ 0 ] = 10;
    vint[ 1 ] = 20;
    vint[ 2 ] = 30;

    // Display elements of the vector:
    vector< int >::iterator it;
    for ( it = vint.begin(); it != vint.end(); ++it ) {
        // Like pointer, iterator is dereferenced to
        // access the value of the element pointed by it:
        cout << " " << *it;
    }
    return 0;
}
// Output: 10 20 30

STL library is also using the iterator concept for iterator adaptors. For example, back_insert_iterator inserts an object after the last element of a container:


#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
int main (int argc, char* argv[])
{
    vector< int > vdummy;    // vector with no elements
    vector< int > vint( 3 ); // vector with 3 integer numbers
    vint[ 0 ] = 1;
    vint[ 1 ] = 2;
    vint[ 2 ] = 3;

    // insert values from vint into dummy:
    back_insert_iterator< vector< int > > inserter( vdummy );
    copy(
        vint.begin(),
        vint.end(),
        inserter
        );

    // Display results:
    for ( int idx = 0; idx < vdummy.size(); ++idx ) {
        cout << " " << vdummy[ idx ];
    }
    return 0;
}
// Output: 1 2 3


std::string


A string is STL container designed to accommodate sequence of characters, that is, text. Strings provide built-in features to operate on fragments of text in a more intuitive way than zero-terminated character arrays, commonly known as C-strings. For example,


#include <cassert>
#include <string>
using namespace std;
int main (int argc, char* argv[])
{
    string str( "Hello" );
    for ( int idx = 0; idx < str.length(); ++idx ) {
        // Access and modify individual characters:
        str[ idx ] = toupper( str[ idx ] );
    }
    assert( str == "HELLO" );

    str.append( 3, '!' ); // add three exclamation points
    assert( str == "HELLO!!!" );

    int pos1 = str.find( "L" );  //    HELLO!!!
    int pos2 = str.rfind( "L" ); //      ||
    assert( pos1 == 2 );         // pos1-''-pos2
    assert( pos2 == 3 );

    // Get 2-character long substring starting at pos1:
    assert( str.substr( pos1, 2 ) == "LL" );

    str.replace( pos1, 3, "YDAY" ); // HE...!!!
    assert( str == "HEYDAY!!!" );   //   '-----pos1
    return 0;
}