You are on page 1of 4

Overview

Overview

tandard Template Library (STL) is understand both the interface and

S
a powerful template library for implementation of your symbol table: only
C++, which is used extensively in you know your program well, as it is non-
the industry. It provides generic, standard.
fundamental data structures and Instead, when you use STL for
algorithms useful for most of the programs. So, implementing your symbol table, you can make
it avoids reinventing the wheel and provides use of a map (or a multi-map) container; use
code that is well tested, versatile, efficient and various generic algorithms (binary_search, sort
generic. For example, if you want to write a etc) to operate on it; and make use of iterators
symbol table for your toy compiler, you can go to traverse the table as required. To be specific:
ahead and write your own symbol table.  You need not write a full-fledged
However, this approach suffers from several implementation of the symbol table: you can
disadvantages: concentrate on solving the actual, specific
 The effort required to write a full-fledged, problem of providing a symbol table.
effective and efficient symbol table is  You can reasonably expect the STL library
substantial. you have to be rigorously tested.
 The hand-written code needs to undergo  Though STL is a generic library, it is
rigorous testing (since the symbol table is a designed with efficiency in mind. It is in fact
very important piece of a compiler). Finding a very efficient library.
and fixing problems can take significant  STL is an industry standard and widely used
amounts of time and energy. library—so you can have any competent C++
 The data structure needs to be efficient and programmer get some exposure to the STL
effective: achieving this is not easy. library. A programmer who may possibly
 If some other programmers are assigned to maintain or extend your symbol table will
maintain or extend your symbol table in the find it easy to understand the
future, they will find it very difficult to implementation of your symbol table.

A Ready
Reckoner for the
Standard
Template
Library
Learn about the many benefits of the Standard Template
Library in this introductory article.

78 MAY 2007 | LINUX FOR YOU | www.linuxforu.com

CMYK
Overview

The list is not exhaustive, yet it covers a few very checked. On the other hand, the at method does range
important reasons to (re)use standard components as checking and throws out_of_range exception if needed.
much as possible. deque: This is basically a double-ended queue. If we
However, just like C++, STL is designed for experienced want to grow/shrink in a vector, we can do it only at one
programmers: it requires some expertise to make use of its end. But with deque, we can do it at both the ends. It
full potential and there are lots of traps and pitfalls that a provides the same accessing efficiency as the vector but
novice can easily fall into. Also, it is a generic library whose the allocation efficiency comparable with a list.
design is inspired by the functional programming paradigm: it list: Arrays are optimised for random access but are
is not object-oriented! Hence, you need some understanding inefficient when it comes to inserting and deleting
and experience about the design philosophy and problem- elements in the middle; so are the vector and deque. For
solving approach of STL to make the best use of it. operations requiring intensive insertions and deletions, use
a list, which is very efficient for these operations (and is
STL components internally implemented as a double-linked list). With a
STL consists of three main parts: list, you cannot have random access to the data and so the
 Containers (like a stack) [] operator is not overloaded.
 Generic algorithms (like a sort) map and set: Both store the elements along with a
 Iterators (similar to the use of pointers) unique key. In most of the cases, these two are
These components are designed such that they are interchangeable. The only difference is that in a set, the
ignorant of specific details of other components, so that values are irrelevant and we keep track of the keys only.
they can be combined together as the need arises. Here They provide an important functionality, which the
lies the secret of the power of STL: the ability to sequence containers do not provide—the find operation.
seamlessly combine the three to fit our need. multimap and multiset: These are extended
Containers are objects that can hold other objects. We versions of a map and set respectively. In a map and set,
can use any type of object with these containers, but the keys should be unique. But a multimap and multiset do
generally, it is assumed that an object that is used with a not have this constraint. All the operations of their
container has the following defined for the object: respective counterparts are supported here also.
 copy constructor  assignment operator
 == operator  < operator Algorithms
This is because whenever an object is used with a The <algorithm> header file provides us with many of the
container, a copy of the object is created and only the copy is algorithms that we use in our day-to-day programming (and
present in the container; so we need the copy constructor lots of not so obvious ones, too). For example, most of us
and assignment operator to be defined. Also, many of the have ended up writing our own versions of a sort, search, find,
algorithms used by the containers need a comparison etc, and STL allows us to reuse the code and helps us to
between objects; so we need == and < overloaded operators. concentrate on more creative aspects of programming. Let us
look at an example of using a fundamental operation—swap:
Containers
Containers are the data-structures in which we can store #include <iostream>
objects. Basically, we have two kinds of containers: #include <algorithm>
 Sequence containers: These containers store and using namespace std;
retrieve data in a sequential fashion. They include the
simple array, list, vector and deque. int main(){
 Associative containers: The elements of these string this = “this”, that = “that”;
containers are associated in some manner. The std::swap(this, that);
associative containers are map, multimap, set and cout<< “this = “<< this<< “and that = “<< that<< endl;
multiset. They rely heavily on comparison operators, }
because the objects are stored in a sorted order. // prints: this = that and that = this
In other words, sequence containers can hold elements
of the same type, whereas associative containers are Algorithms in STL are basically of three types:
capable of holding a key-value pair.  non-modifying (sequence); for example, find
Let’s look at a few of the containers in STL:  modifying (sequence); for example, fill
vector: Vector can be treated as an array with the capability  sorted (sequence); for example, sort
of growing or shrinking dynamically. This can be safely used Non-modifying algorithms are for read-only/traversing
instead of arrays. The elements can be accessed in two ways: functionality that essentially doesn’t modify the content of
 using the overloaded operator [] the containers. In short, they don’t modify the sequence on
 using the method at which they operate. Note that ‘sequence’ here refers to the
The first one is easier and faster to use, but it is not range ‘sequence containers’—referring to containers with

www.linuxforu.com | LINUX FOR YOU | MAY 2007 79

CMYK
Overview

elements of the same type T (homogeneous elements), for int arr[] = {1, 4, 9, 16, 25}; // some values
example, std::vector<T>, std::list<T>. int * pos = find(arr, arr+4, 36);
Modifying algorithms may alter the sequence on which if(pos == (arr+4))
they operate. The last kind of algorithms work on sorted std::cout<< “Searched element not found in the array”;
sequences—for example, the binary_search algorithm. else
As you can easily guess, swap comes under modifying std::cout<< “Found in the position “<<
sequence algorithms. (pos - arr);
// prints:
Iterators // Searched element not found in the array
Iterators are generalised pointers and act as the glue between
containers and algorithms. STL algorithms are written in This aspect of iterators is very important to understand
terms of iterator parameters, and STL containers provide as it is used extensively in STL.
iterators that can be plugged into algorithms. Generally,
iterators point to a location within a container. Why iterators?
Iterators have a pointer-like syntax (in many cases, Let’s suppose that you want to go to the beginning of a list.
iterators are indeed implemented as pointers internally). You can use the member function front, which returns an
Thus, generic algorithms can handle arrays and pointers - iterator (reference) to the first element of the array, and in
this is of significant importance since we need not throw this way proceed with your usual work. You might wonder
away our C-style arrays for the sake of using this library. why we need iterators when member functions will do.
For example, in the find generic algorithm, we can either Let us take the generic algorithm sort, which is used
use arrays or container classes: for sorting, say, a vector. If we hadn't had iterators, then
we would have had to write a separate algorithm for each
#include<iostream> and every container. So we pass to this algorithm two
#include<vector> iterators as parameters, which point to the start and end of
using namespace std; the sequence to be sorted, respectively. You will notice
that, with this mechanism we are able to use any sort of
int main(){ containers with an algorithm.
int arr[] = {1, 4, 9, 16, 25}; // some values
int * arr_pos = find(arr, arr+4, 9); Important concepts for using STL
std::cout<< “array pos = “<< arr_pos - arr << endl; Since STL provides a whole new way of solving the
// find the first occurrence of 9 in the array problems in C++, there are many concepts that need to be
understood to learn and to make best use of STL.
vector<int> int_vec; Function objects: Using C style function pointers is
for(int i = 1; i <= 5; i++) not type-safe and isn’t object oriented. An alternative in
int_vec.push_back(i*i); C++ is to use ‘function objects’. By overloading the
vector<int>::iterator vec_pos = function call operator (), we encapsulate a function and
find (int_vec.begin(), pass it on to some other functions. The advantages of using
int_vec.end(), 9); a function pointer (also referred to as ‘functor’) are:
std::cout<< “vector pos = “<< (vec_pos - int_vec.begin());  typesafe and object oriented
// find the first occurrence of 9 in the vector  efficient, as it can be inlined
}  reusable, as it can be generic
// prints: The idea is to overload the () operator so that the object
// array pos = 2 can be used as if it were a function. Overloaded () can have
// vector pos = 2 any number of arguments / any return type. For example:

Here, note that we are using iterators as ‘pairs’. This is how #include<iostream>
we generally make use of iterators: a way of marking the using namespace std;
beginning and the end of the sequence to be operated on.
However, unlike pointers, there is no iterator equivalent of class printClass{
‘null’—it’s simply undefined behaviour to dereference an public:
iterator pointing to some illegal value. Traditionally, returning template<class T> void operator() (T t)
null (0) is how we indicate that a value searched is found or { cout<< t << endl; }
not. Since null cannot be used for iterators, how do we indicate };
that the value searched is not found? For that, the element
‘one-past’ the end is used (note that it is not illegal for a pointer template<class T> void print(T type, printClass &p){
to point ‘one-past’ the last element in an array). For example: p(type); // invoke the () operator

80 MAY 2007 | LINUX FOR YOU | www.linuxforu.com

CMYK
Overview

} Adaptors: Adaptors, as the name itself hints, is a


component that adapts (modifies) an existing interface of
int main(){ a component to expose a different one suitable for some
int i = 10; other purpose. There are three types of adaptors:
float f = 10.0;  Sequence adaptor: The interface of a container is
printClass p; exposed in a different way. A classic example for
print(i, p); sequence adaptors is from STL itself: stack is built on
print(f, p); modifying the interface of deque.
}  Iterator adaptor: When the interface of an iterator is
// prints exposed in a different way, it is an iterator adaptor.
// 10  Function adaptor: Function adaptors are function
// 10 objects—depending on the function object (say a
‘negator’ or a ‘predicate object’) passed, the behaviour
This simple program makes use of function objects to of the algorithm may change.
print objects of any type (provided that they override Pseudo-destructor: STL is fully generic and it needs
ostream << ()). many extensions/modifications, mostly related to
Plug-compatibility: Plug-compatibility in programming templates to support it. For example, ‘pseudo destructor’
jargon means that a generic algorithm and a container can is just a syntactic convenience: it enables primitive type to
be plugged (used) together. For example, vector and sort be used with containers and algorithms.
are plug-compatible (you can apply sort function on a
vector), whereas you cannot use sort for a list—for that template <typename T> void callDest(T &t){
you have to use the sort member function (of list). t.T::~T();
Theoretically, it is possible for generic sort to be plugged };
with list—but it will affect the efficiency of the code. So,
list provides a separate member function to achieve that. class Test{
public:
sort(vector1.begin(), vector1.end()); ~Test(){
// ok, works std::cout<<“calling the destructor”<<endl;
vector1.sort(); }
// no sort member function };

sort(list1.begin(), list1.end()); int main(){


// no, generic sort should not be used with list int i;
list1.sort(); callDest(i);
// ok, works // doesn’t issue compiler error
// when t.T::~T() resolves to t.int::~int()
Nearly containers: STL is a generic library with // this has no effect in the code generated.
components written for general-purpose use. But not all
components are ‘truly generic’. There are three Test t;
components in STL that are written for specific types/ callDest(t);
purposes in mind. For example, bitset is not a generic set // results in calling the destructor explicitly
component—it is specifically designed to handle bit // prints:
information. Such containers are referred to as ‘nearly // calling the destructor
containers’, and they are for specific purposes and not }
really generic in nature. The other two containers are
valarray—designed specifically for numeric Hopefully, this introduction to Standard Template
computations, and string, an alternative for null Library would help a novice understand its basics, in detail.
terminated C-style strings. We will cover further details on the same topic later. Till
Predicate objects: When a function object returns a then, happy reading...
Boolean value, it is referred to as a ‘predicate object’. In STL,
<functional> contains many such predicate objects and they
can be used in generic algorithms to change their behaviour. By: S.G. Ganesh is an engineer in Hewlett-Packard’s C++
For example, the sort method implicitly takes the less<int> compiler team. He has authored a book “Deep C” (ISBN 81-
predicate as the third argument (to sort the elements in 7656-501-6). He is also a member of the ANSI/ISO C++
ascending order). To change this default behaviour of sort, it Standardisation committee (JTC1/SC22/WG21), representing
is enough to pass some other predicate! HP. You can reach him at sgganesh@gmail.com.

www.linuxforu.com | LINUX FOR YOU | MAY 2007 81

CMYK

You might also like