Headlines News :
Home » , , » Learn C++ in 12 Days - Day 11

Learn C++ in 12 Days - Day 11

Written By Ente Malayalam on Friday, May 24, 2013 | 5/24/2013


DAY– 12            All these days we were working with encapsulation which is completely dependent on the data type. Now lets say we are going to work with a specific data structure like stack. We know that stack can be an integer stack or character stack or any other data type. lets say in your program you want to utilize an integer stack and a character stack. As you know, even though the data type are different, the basic logic behind both these stacks are exactly the same. But even then we are forced to write two different stacks, one for integer and another one for character. As far as a programmer is concerned this is indeed a tedious task. So if ever we have a method by which we can write a single class and make it work for all data types, wont it be better. Yes right. ie) writing generic programs or programs independent of data type.That’s what we are going to do today. And it is done using something known as Templates.
            Since C++ supports procedural as well as object oriented approach, we will use templates to implement generic functions as well as generic classes. As usual lets start with generic functions.

Generic functions:
           
            A generic fn defines a general set of operations that will be applied to various types of data. The type of data the fn will operate on will be passed to it as a parameter. Best real time eg is sorting algorithms. The algorithm is going to be same if it s applied to an array of integers of array of floats. It s jus the type of data being sorted is different.
            By creating a generic fn, u r jus defining the nature of the algorithm, independent of any data. Once u have done this, the compiler will automatically generate the correct code for the type of data that is actually used when u execute the fn. In other words, when u create a generic fn u are creating a fn that can automatically overload itself.
            A generic fn is created using the keyword template. The general form of a template fn definition is

template <class Ttype> return type fn name(parameter list)
{
            //body of fn
}

Here Ttype is the place holder name for a data type used by the fn and this name will be used with in the fn. This place holder will be automatically replaced with an actual data type when it creates a specific version of the fn.

Lets take a simple swap as an example and explain things based on that







#include <iostream>
using namespace std;

template <class Ttype>
void Swap_Values(Ttype&, Ttype&);

int main()      {
        int a = 10, b = 20;

        cout << "Value of a before swapping = " << a << endl;
        cout << "Value of b before swapping = " << b << endl;

        Swap_Values <int> (a, b);

        cout << "Value of a after swapping = " << a << endl;
        cout << "Value of b after swapping = " << b << endl;

        float c = 12.2, d = 22.2;

        cout << "Value of c before swapping = " << c << endl;
        cout << "Value of d before swapping = " << d << endl;

        Swap_Values <float> (c, d);

        cout << "Value of a after swapping = " << c << endl;
        cout << "Value of b after swapping = " << d << endl;

        char e = 'a', f = 'b';

        cout << "Value of e before swapping = " << e << endl;
        cout << "Value of f before swapping = " << f << endl;

        Swap_Values <char> (e, f);

        cout << "Value of e after swapping = " << e << endl;
        cout << "Value of f after swapping = " << f << endl;

        return 0;
}
template <class Ttype>
void Swap_Values(Ttype &p, Ttype &q)    {
        Ttype Temp;
        Temp = p;
        p = q;
        q = Temp;
}

Now lets analyse the above program. The statement
template <class T> void Swap_Values(Ttype &, Ttype &)
tells the compiler two things. Firstly the template is being created and that a generic definition is beginning.
            Here Type is the place holder. Now in our main we have three calls for the fn Swap_Values(). Lets take the first one. When the compiler sees the fn call, first it checks what the data type of a and b is . then that data type is passed as an argument.ie) it replaces T by int. Then it generates a swap function corresponding to integer and stores it in memory.
            Similarly in the second case, T will be replaced by float and a version of swaparg() corresponding to float s will be created and kept in another section of memory and the same process is repeated for char also.
            Now u should be very familiar with some words related to templates. First one is template functions.
  1. A generic function is also known as template function. 
  2. When the compiler creates a specific version of this function, it is said to have created a specialization. This is also called a generated function.
  3. The act of generating a function is referred to as instantiating it. Or generated fn is a specific instance of a template fn.

Now lets go back to the above program itself. Let me include one more function call there to swap two integers again.
#include <iostream>
using namespace std;

template <class Ttype>
void Swap_Values(Ttype&, Ttype&);

int main()      {
        int a = 10, b = 20;

        cout << "Value of a before swapping = " << a << endl;
        cout << "Value of b before swapping = " << b << endl;

        Swap_Values <int> (a, b);

        cout << "Value of a after swapping = " << a << endl;
        cout << "Value of b after swapping = " << b << endl;

        float c = 12.2, d = 22.2;

        cout << "Value of c before swapping = " << c << endl;
        cout << "Value of d before swapping = " << d << endl;

        Swap_Values <float> (c, d);

        cout << "Value of a after swapping = " << c << endl;
        cout << "Value of b after swapping = " << d << endl;

        char e = 'a', f = 'b';

        cout << "Value of e before swapping = " << e << endl;
        cout << "Value of f before swapping = " << f << endl;

        Swap_Values <char> (e, f);

        cout << "Value of e after swapping = " << e << endl;
        cout << "Value of f after swapping = " << f << endl;

        int g = 100, h = 200;

        cout << "Value of g before swapping = " << g << endl;
        cout << "Value of h before swapping = " << h << endl;

        Swap_Values <int> (g, h);

        cout << "Value of g after swapping = " << g << endl;
        cout << "Value of h after swapping = " << h << endl;

        return 0;
}

template <class Ttype>
void Swap_Values(Ttype &p, Ttype &q)    {
        Ttype Temp;
        Temp = p;
        p = q;
        q = Temp;
}

In this case, the compiler won’t be creating a new version for this integer fn call, instead it will be retrieving the integer fn generated and kept in the memory when the swap call Swap_Values(a, b) occurred.

A function with two generic types:
           
            In the above eg we were dealing with a single data type. Now it is even possible to use two data types in the same template fn. Lets c an eg:

template <class T1, class T2> void display(T1, T2);
main()
{
        int i = 10;
        char a = 'a';

        display(i, a);
}


template <class T1, class T2>
void display(T1 p, T2 q)
{
        cout << p << endl << q << endl;
}

Here first compiler sees the fn call and replaces T1 with int and T2 with char and creates a version corresponding to it. Rest all remains the same.

Consider the below program for finding the maximum of two numbers

#include <iostream>
using namespace std;

template <class Ttype>
void Max_Values(Ttype,Ttype);

int main()      {
        int a = 10, b = 20;
        Max_Values(a, b);

        char d[] = "world", c[] = "hello";
        Max_Values(c, d);
}

template <class Ttype>
void Max_Values(Ttype v1, Ttype v2)     {
        if (v1 > v2)
                cout << "greater one is " << v1 << endl;
        else
                cout << "greater one is " << v2 << endl;
}

Here the function works perfectly for integers or even for float or double, but when it comes to strings, the comparison goes wrong. When we use > operator, it simply checks the address where the strings are placed and never the ascii values of the characters in the string. Usually for string comparison, we have to go for our strcmp function. So the above function doesn’t hold good for strings.
            In such cases if ever we can create a situation where in , when data type is string, it calls another function for the comparison and not this function, wont it be better. Such a flexibility is also there in templates and it can be done in two different ways.
1.      Explicit specialization
2.      Template overriding

Explicit specialization :

            The syntax is
                       
                        template <>
                        return_type function_name( arguments);

modifying the above program :

#include <iostream>
using namespace std;

template <class Ttype>
void Max_Values(Ttype,Ttype);

template <>
void Max_Values(char*, char*);

int main()      {
        int a = 10, b = 20;
        Max_Values(a, b);

        char d[] = "abc", c[] = "hello";
        Max_Values(c, d);
}

template <class Ttype>
void Max_Values(Ttype v1, Ttype v2)     {
        if (v1 > v2)
                cout << "greater one is " << v1 << endl;
        else
                cout << "greater one is " << v2 << endl;
}

template <>
void Max_Values(char *p, char *q)       {
        int v;
        v = strcmp(p, q);
        if(v == 0)
                cout << "Both are equal" << endl;
        else if(v > 0)
                cout << "the bigger string is " << p << endl;
        else
                cout << "the bigger string is " << q << endl;
}
             
Explicitly overloading a generic function:                                                                             
                                                                                   
void swaparg(int &p, int &q);
template <class T> void swaparg(T &p, T &q);
main()
{
        int a = 10, b = 20;
        cout << a << endl << b << endl;
        swaparg(a, b);
        cout << a << endl << b << endl;
}
void swaparg(int &p, int &q)
{
        cout << "explicit fn" << endl;
        int temp;
        temp = p;
        p = q;
        q = temp;
}
template <class T>
void swaparg(T &p, T &q)
{
        cout << "template fn" << endl;                
         T temp;
        temp = p;
        p = q;
        q = temp;
}               

can u tell me what the output is :
10
20
explicit fn
20
10
so what s happening here. In the above program u r writing a template fn as well as a normal fn. That s u r overloading the swap fn. But c the output. Even though the template fn is present, the compiler calls only the explicitly overloaded fn. Thus the compiler doesn’t generate the version of generic swapargs function because the generic fn is overridden by the explicit overloading.
            So now the use of this explicit overloading: it allows you to tailor a version of a generic fn to accommodate a unique situation – perhaps to take advantage of some performance boost that applies to only one type of data.  
            Now remember it is even possible to overload your template functions. C it in “complete reference” and if u have any doubt u come and ask me ok.

Generic function restrictions:               

            Generic fn are similar to overloaded fn, but they have one restriction. When fn are overloaded, we can have different actions performed with in the body of each fn. But a generic fn must perform the same general actions for all versions, only data type should vary.         

Generic classes:                                    

            It is possible to write generic classes in the same manner u which u created generic fn. When u do this u will be creating a class that defines all the algorithms used by a class, however the actual type of data being manipulated will be specified as a parameter when objects of that class is created.

Lets take a stack eg. Stack is actually a general algorithm which can be used to push and pop integers, floats or char etc. so it will be always better if we can create a generic class for that. Lets write the program and explain things based on that.

const int MAX = 20;
template <class S>
class stack     {
        private:
                S stk[MAX];
                int tos;
        public:
                stack() {
                        tos = 0;
                }
                void push(S data)       {
                        if (tos == MAX) {
                                cout << "stack is full" << endl;
                                return;
                        }
                        else    {
                                stk[tos] = data;
                                tos++;
                        }                       
            }
                S pop() {
                        if (tos == 0)   {
                                cout << "stack is empty";
                                return 0;
                        }
                        else    {
                                tos--;
                                return stk[tos];
                        }
                }
};
main()
{
        stack <int> s1;
        s1.push(10);
        s1.push(20);
        s1.push(30);
        cout << s1.pop() << endl;
        cout << s1.pop() << endl;  
        cout << s1.pop() << endl;
        stack <char> s2;
        s2.push('a');
        s2.push('b');
        s2.push('c');
        cout << s2.pop() << endl;
        cout << s2.pop() << endl;
        cout << s2.pop() << endl;
}               

now if I am writing it the fn outside, u have to take care of one thing. Whenever the class name comes u have to specify the place holder format there as shown in the following program.
const int MAX = 20;
template <class S>
class stack     {
        private:
                S stk[MAX];
                int tos;
        public:
                stack() {
                        tos = 0;
                }
                void push(S data);
                S pop();
};
template <class S> void stack <S> :: push(S data)       {
        if (tos == MAX) {
                cout << "stack is full" << endl;
                return;
        }
         else    {
                stk[tos] = data;
                tos++;
        }
}
template <class S> S stack<S> :: pop()
{
        if (tos == 0)   {
                cout << "stack is empty" << endl;
                return 0;
        }
        else    {
                tos--;
                return stk[tos];
        }
}
main()
{
        stack <int> s1;
        s1.push(10);
        s1.push(20);
        s1.push(30);
        cout << s1.pop() << endl;
        cout << s1.pop() << endl;  
        cout << s1.pop() << endl;
        stack <char> s2;
        s2.push('a');
        s2.push('b');
        s2.push('c');
        cout << s2.pop() << endl;
        cout << s2.pop() << endl;
        cout << s2.pop() << endl;
}       








An eg with two generic data types:

Let s c an eg:

template <class T1, class T2>
class sample    {
        private:
                T1 x;
                T2 y;
        public:
                sample(T1 p, T2 q)      {
                        x = p;
                        y = q;
                }
                void display();
};
template <class T1, class T2> void sample <T1, T2> :: display()
{
        cout << x << endl;
        cout << y << endl;
}
main()    
{
        sample <int, char> obj(10, 'a');
        obj.display();
}

Share this article :

0 comments:

Post a Comment

 
Support : Creating Website | Johny Template | Maskolis | Johny Portal | Johny Magazine | Johny News | Johny Demosite
Copyright © 2011. Education World - All Rights Reserved
Template Modify by Creating Website Inspired Wordpress Hack
Proudly powered by Blogger