Advanced C++ : Standard Template Library (STL)

Discussion in 'C++' started by BiplabKamal, Jun 3, 2016.

  1. BiplabKamal

    BiplabKamal Member

    Joined:
    Dec 30, 2015
    Messages:
    37
    Likes Received:
    7
    Trophy Points:
    8
    Occupation:
    Software Engineer
    Location:
    Kolkata
    As the name suggests STL is the part of the C++ standard library which provides a rich set of template classes and template functions to make application development faster. It is a software library, a very important part of the standard library and influenced other parts of the standard library. STL library implementation has used the power of generic programming and projected the huge potential of generic programming to the programmers.

    STL provides a ready-made set of common template classes and functions which can be instantiated for any in-built types as well user defined types supporting some elementary operations like copy or assignment operations. This is achieved with generic programming using templates. Templates provide compile time polymorphism which is more efficient than run-time polymorphism. Modern c++ compilers are tuned to optimize any abstraction penalty arising out of using templates heavily.

    Standard Template Library (STL) has four components briefed below:

    Containers



    Contains template classes to create objects which can hold collection of data of different types both inbuilt and user defined. For example vector is a container class which stores any kind of objects in a contiguous memory.

    A container is an objects that stores collection of data. They are implemented as class templates, which gives a great flexibility in supporting different types of elements. The containers manages the storage for its element and provide access to them via iterators and member functions. The STL contains two types of container classes, the standard sequence containers and standard associative containers. Sequence containers include array,vector, list and deque which store and manipulate collection of elements which are sequential in nature. Associative containers store and manipulate sorted and unsorted collection of data which are not sequential in nature. These include set, map, multiset, multimap. There are also some container adapters like queue, stack and priority_queue. Adapter containers classes are not fully containers but they internally use an object of some other container class and provide some specific interface. Adapters allow access to it’s elements independent of their underlying containers. For details of containers and their methods see Containers page.

    Iterators



    Cntains template classes to create iterator objects which points to an element of a container object and provide operators to traverse through all elements of that container object and access the values of the elements. For example vector.begin() returns an iterator object which points to the first element of the vector.

    Iterator is an object which points to an element in a range (collection) of objects and has the capability to traverse through the elements of that range with a set of operators like increment(++), decrement(--) and dereference (*) operators. The simple form of iterator is the pointer which can point to an element of an array and can iterate through the elements using increment operator(++). Pointer works for simple collection like array but not for most STL containers. Every container type in STL has it’s own iterator type. You need not to create an iterator object, rather you can get an iterator object from a container using member function like begin() or end(). Normally an iterator type name of a container is long and complex. But good news is that you need not to type those type name because every container supporting iterator also have a typedef member to specify it’s iterator type. For example the type of iterator supplied by the std::vector<int> class is std::_Vector_iterator<std::_Vector_val<std::_Simple_types<int>>>. So if you want to acquire and use the iterator of vector of integer the code will look like :
    Code:
    std::vector<int> v{ 1,2,3,4,5 };
    std::_Vector_iterator<std::_Vector_val<std::_Simple_types<int>>> itr_begin = v.end();
    
    But using the typdef member of vector std::vector<int>::iterator you can write above line as shown below:
    Code:
    std::vector<int>::iterator itr_begin = v.begin();
    
    Wait, there is more easier way of doing this using automatic type deduction using ‘auto’ key word. You don’t need to type the type name of the iterator at all! See, the following line will do the same thing
    Code:
    auto itr_begin = v.begin();
    
    Depending on functionality of STL iterators they are divided in five categories. The basic functionality of all iterators are copy-constructable, copy-assignable and destructible and can be incremented.
    1. Input Iterator: It is used to iterate and read the elements. Supports equality(==) and inequality(!=) comparison operators. Can be dereferenced as an rvalue.
    2. Output Iterator: Is used to iterate and write the elements. It can be dereferenced as lvalue
    3. Forward Iterator: It has all the functionality of input and output iterators and can be moved forward through elements.
    4. Bidirectional Iterator: It is same as forward iterator and can move backward also.
    5. Random Access Iterator: It has all functionality of bidirectional iterator and also has the capability to access ranges non-sequentially. This iterator has similarity to standard pointer. It support arithmetic operators (+ and -), inequality comparison operators (< , > , <= and >=), compound assignment operators(+= and -=) and offset dereference operator ([]).
    For details of Iterators and their methods see Iterators page.

    Functors



    Contains functor classes to create functors. The underlying functions are constant and do not modify the arguments which takes 0 to 2 arguments. They take some action or return a value based on the arguments passed. Though they are independent functor classes, they are mainly used by algorithm functions. For example greater is a template funtor class whose function operator takes two objects and compare them. It returns the comparison result(1st arg>2nd arg) of the arguments. For detail of each functor see Functors page.

    Algorithms



    Contains algorithm functions which are template functions and operate on iterators given by containers and uses functors as helper functions. Algorithms work on a range of elements specified by iterators and uses predicate or comparator functors to operate on elements. For example sort() algorithm function sort a range of elements. It takes 3 arguments: the iterator pointing to the first element of the range, the iterator pointing to the last elements of the range and a function object called predicate to compare two elements. The predicate decides the order of sorting. For detail of each algorithm see Algorithms page.

    Collaboration of STL components



    These four components work together with collaboration. Containers hold data, mange storage, provide interface to add, remove and access elements. Containers also expose iterators to the client code, normally algorithms. Algorithm functions accept functors and iterators and apply the algorithms to the range of elements specified by the iterators.

    [​IMG]

    So knowing one component is not complete without knowing other components. Containers and iterators goes together. Algorithms goes together with iterators and functors.

    Following program is using all the components

    Code:
    #include<vector> //Header for vector container 
    #include<iostream>
    #include<functional> // Header for functors
    #include<algorithm> // Header for algorithm function
    using namespace std;
    struct Employee
    {
        int empid;
        string empname;
        // Less than operator
        bool operator<(const Employee e) const
        {
            return empname < e.empname;
        }
        // Greater than operator
        bool operator>(const Employee e) const
        {
            return empid > e.empid;
        }
    };
    
    int main()
    {
        vector<Employee> emploees;
        //Add elements
        emploees.emplace_back(Employee{ 1,"Sunil Kumar" });
        emploees.emplace_back(Employee{ 2,"Pawan Sharma" });
        emploees.emplace_back(Employee{ 3,"Pawan Kumar" });
        emploees.emplace_back(Employee{ 4,"Sunil Roy" });
        emploees.emplace_back(Employee{ 5,"John" });
        //Lamda expression to print each element
        auto printEmployee = [](Employee e) {cout << "Employee Id =" << e.empid << " Employee Name = " << e.empname.c_str() << endl;};
    
        cout << "Initial employees: " << endl;
        // Using algorithm function std::for_each. begin and end functions of vector return iterators.
        //It takes a function object as the third parameter which is applied on each element
        // Note that we are using a user defined functor
        for_each(emploees.begin(), emploees.end(), printEmployee);
    
        // Using algorithm function std::sort. begin and end functions of vector return iterators
        sort(emploees.begin(), emploees.end()); // Sort ascending by default
        cout << "Employees after default sorting: " << endl;
        for_each(emploees.begin(), emploees.end(), printEmployee);
        sort(emploees.begin(), emploees.end(),greater<Employee>()); //Sort descending
        cout << "Employees after custom sorting: " << endl;
        for_each(emploees.begin(), emploees.end(), printEmployee);
        return 0;
    }
    
    Output of the program:
    Code:
    Initial employees:
    Employee Id =1 Employee Name = Sunil Kumar
    Employee Id =2 Employee Name = Pawan Sharma
    Employee Id =3 Employee Name = Pawan Kumar
    Employee Id =4 Employee Name = Sunil Roy
    Employee Id =5 Employee Name = John
    Employees after default sorting:
    Employee Id =5 Employee Name = John
    Employee Id =3 Employee Name = Pawan Kumar
    Employee Id =2 Employee Name = Pawan Sharma
    Employee Id =1 Employee Name = Sunil Kumar
    Employee Id =4 Employee Name = Sunil Roy
    Employees after custom sorting:
    Employee Id =5 Employee Name = John
    Employee Id =4 Employee Name = Sunil Roy
    Employee Id =3 Employee Name = Pawan Kumar
    Employee Id =2 Employee Name = Pawan Sharma
    Employee Id =1 Employee Name = Sunil Kumar
    
    We will discuss containers, iterators, functors and algorithms in detail in next chapters. You can go to and fro whenever needed. Also you will need to consult STL reference manual for each individual class and functions. The proper sequence of learning STL will be containers, iterators, functors and algorithms. You should first get familiarity with the pattern of STL components. For example Containers will work with data types which support some specific operators. So if you want your own class objects to be stored in STL containers your class should overload those operators. Again most of the sequence containers and associative containers have similar functionality, member functions and iterator support. Iterators are generic and have a hierarchical structure. Algorithms are generic and works with iterators irrespective of their containers. Functors are independent of containers, iterators and algorithms.
     
    Last edited by a moderator: Jan 21, 2017

Share This Page

  1. This site uses cookies to help personalise content, tailor your experience and to keep you logged in if you register.
    By continuing to use this site, you are consenting to our use of cookies.
    Dismiss Notice