Module 9 :Static Polymorphism
Introduction
Static polymorphic behavior is realized at compile time. A class template acts as a generic class definition from which new class types can be defined by a simple change of parameter types. Whereas a normal class is used to create objects, a class template is used to define new class types, from which objects are then created. The compiler uses the types you supply, combined with the class template, to create a wholly new class type.Understanding how to declare, implements, and use class and function templates will pay big dividends in many ways. Primarily, it provides you with a mechanism to write generic code. Learning to write generic code can potentially save you a lot of work.
Learning Objective
By the end of this Module, you will be able to:
- Explain how to achieve static polymorphic behavior through templates
- Explain how to write generic code using templates
- Explain how to declare and implement function templates
- Explain how to declare and implement class templates
- Explain how to declare and implement class member function templates
Definition of Template
A template defines a related set of classes or functions. The related set of classes or functions defined by a template share the same code structure and functionality. The class or function template can then be used to declare a new class or function type.Function Templates
A function template is a generic function declaration and definition from which different versions of the function can be created by the compiler based on the argument types used to call the function. If you think this sound a lot like overloaded functions you are right. Function templates and overloaded functions are related as you will soon see.Class Templates
A class template is a generic class declaration and definition from which different, but related, class types can be created by the compiler based on type parameters.Structure Templates
A structure template is like a class template but using structures instead.How Templates Work: An Analogy
When you declare and define a template you are creating a generic version of whatever piece of code you are writing, be it a function or a class. In the declaration and definition of the template you will use one or more identifiers as type placeholders. These type placeholders are similar in function to the placeholders in a form letter generated with a word processor.The below figure illustrates a simple mail merge operation. A master letter is created with placeholders for certain data elements. The structure of the data source is mapped to the master letter by the placeholder identifiers name and age. When the mail merge function is executed, the data source is merged with the master letter to yield the finished letters. The master letter is a generic document that can be reused to generate many specific letter instances. Class and function templates work in similar fashion. A generic function or class is declared and defined. Placeholders are inserted into the code to reserve spots for actual data types. When specific versions of a template function are required the type substitutions are made based on the types of the arguments used to call the function. In the case of template classes, a special syntax is used when a new template class is declared.
See: https://docs.google.com/open?id=0B_cy2j6cIeqTZ1VSbzJidkdkYlU
Declaring and Using Function Templates
Up until now, if you wanted to create different versions of the same function to operate on different data types you would overload the function. For instance, if you wanted to declare a function named Sum() that took two arguments, added them together, and returned the result you could create several versions of the function like so:int Sum(int val1, int val2);
float Sum(float val1, float val2);
char Sum(char val1, char val2);
</pree>
These three functions can be replaced with one function template.
<pre>
ifndef SUM_TEMPLATE_H
define SUM_TEMPLATE_H
template<class T> T Sum(T val1, T val2){
return val1 + val2;
}
endif
The Sum() function is declared to be a template by the keyword
template appearing on line 4. Following the keyword template in angle
brackets is the keyword class followed by a placeholder identifier named
T. The class keyword as it is used here essentially means “any type”.
The placeholder T will then appear somewhere in the function. It can
appear in more than one place, as it does here in the parameter list.
You can use any valid identifier as a placeholder name, not just T. You
can also declare more than one placeholder.
To use the Sum() function template The Sum() function is called the same
way as normal functions are called. The example below shows a main()
function using the Sum() function on different data types.
include <iostream>
ifndef SUM_TEMPLATE_H
define SUM_TEMPLATE_H
template<class T> T Sum(T val1, T val2){
return val1 + val2;
}
endif
using namespace std;
int main(){
cout<<Sum(3, 25)<<endl;//Integer
cout<<Sum(3.456, 5.786)<<endl; //Float
cout<<Sum('a', 'b')<<endl; //Char
//cout<<Sum(3, 3.5)<<endl; return 0;
}
Using Multiple Placeholders
The Sum() function template declared and defined in the above example used one type placeholder named T to reserve type spots in the function. Because both of the Sum() function’s parameters are reserved with the same placeholder they must be of the same type when the function is called. To illustrate, let us see what happens when the Sum() function is called with an integer and a float argument as shown in the following line of code:cout<<Sum(3, 3.5)<<endl;Error : in function int main() line no 15: no matching function for call to `Sum(int, double) When the Sum() function template is called with two different argument types an error results. This error was produced using the Dev C++ 4.9.9.2. One way to eliminate this error is to declare the Sum() function template to use two different placeholders.
include <iostream>
ifndef SUM_TEMPLATE_H
define SUM_TEMPLATE_H
template<class T, class U> T Sum(T val1, U val2){
return val1 + val2;
}
endif
using namespace std;
int main(){
cout<<Sum(3, 25)<<endl;
cout<<Sum(3.456, 5.786)<<endl;
cout<<Sum('a', 'b')<<endl;
cout<<Sum(3, 3.5)<<endl;
return 0;
}
Notice now there are two type placeholders declared on line 5 of the
above example. T and U. The U placeholder is used for the second
parameter in the Sum() function while the T placeholder is used for the
first parameter and the return value. This will solve one problem but
introduce another. The above Example shows the revised Sum() function
template in use.
Refer to line 14 of the above example. Notice now that the Sum() function is called with the first argument an integer and the second argument a float. By using two placeholders in the function template the error produced by using two different argument types is eliminated. However, the result type of the Sum() function is dictated by the first argument type. Since the T placeholder is used to reserve the type spot for both the first parameter and the return type of the function, whatever type the first argument to the function happens to be will also be the return type of the function. In this example it is an integer. So, the result of calling the Sum() function with the arguments 3 and 3.5 is 6, not 6.5! Notice what happens when the order of the arguments are swapped.
To resolve the ambiguous return type issue simply declare yet another type place holder used specifically to dictate the function’s return type. The below example gives the revised Sum() function with the extra type placeholder V declared and used to reserve the return type.
include <iostream>
ifndef SUM_TEMPLATE_H
define SUM_TEMPLATE_H
template<class T, class U, class V> V Sum(T val1, U val2){
return val1 + val2;
}
endif
using namespace std;
int main(){
cout<<Sum<int, int, int>(3, 25)<<endl;
cout<<Sum<double, double, double>(3.456, 5.786)<<endl;
cout<<Sum<char, char, char>('a', 'b')<<endl;
cout<<Sum<double, int, double>(3.5, 3)<<endl;
return 0; }
Declaring And Using Class Templates
Class templates are used to declare and define a generic class structure. The compiler uses the class template and any types supplied via specialization to build a new class type. Let us begin the discussion of class templates with a simple Foo example. Example below shows the declaration and definition of a class template named Foo.include <iostream>
ifndef FOOTEMPLATEDEF_H
define FOOTEMPLATEDEF_H
template<class T> class Foo{ public: Foo(T _val); virtual ~Foo();
void setVal(T _val); T getVal(); private: T val; };
template<class T> Foo<T>::Foo(T _val):val(_val){}
template<class T> Foo<T>::~Foo(){}
template<class T> void Foo<T>::setVal(T _val){ val = _val; }
template<class T> T Foo<T>::getVal(){ return val; }
endif
using namespace std;
int main(){
Foo<int> f1(1);
Foo<char> f2('d');
cout<<f1.getVal()<<endl;
cout<<f2.getVal()<<endl;
return 0;
}
The declaration of the Foo class templates begins on line 6 with the
keyword template. There is only one template parameter declared named T.
The T is used throughout the class declaration and definition to
reserve a spot for the type declared when the Foo class is specialized.
Referring to line 33 of above example, notice how the Foo class template
is specialized to use an int type. The important point to note in this
example is that Foo<int> and Foo<char> are two distinct
types.
A More Complex Class Template Example
The below Example gives the source code for a template of the
DynamicArray class.
include <iostream>
ifndef _DYNAMIC_ARRAY_H
define _DYNAMIC_ARRAY_H
template<class T> class DynamicArray{
public: DynamicArray(int _size = 5);
virtual ~DynamicArray();
T& operator[](unsigned i);
int getSize();
private: T* its_array;
int size;
};
//////////////////////////////////////////////////////////
template<class T> DynamicArray<T>::DynamicArray(int _size):size(_size){
its_array = new T[_size];
for(int i=0; i<size; i++) its_array[i] = static_cast<T>(0);
}
template<class T> DynamicArray<T>::~DynamicArray(){
delete[] its_array;
}
template<class T> T& DynamicArray<T>::operator[](unsigned i){
if(i >= (size))
{
int newsize = size+10;
T* temp = new T[size];
for(int j = 0; j<size; j++){
temp[j] = its_array[j];
}
delete[] its_array;
its_array = new T[newsize];
for(int j = 0; j<size; j++){
its_array[j] = temp[j];
}
for(int j=size; j<newsize; j++){
its_array[j] = static_cast<T>(0);
}
delete[] temp;
size = newsize;
return its_array[i];
}
else
return its_array[i];
}
template<class T> int DynamicArray<T>::getSize(){ return size;}
endif
using namespace std;
int main(){
DynamicArray<char> d1;
DynamicArray<float> d2;
for(int i=0; i<6; i++){
d1[i] = 'a';
}
for(int i=0; i<6; i++){
d2[i] = (i + .5);
}
for(int i=0; i<d1.getSize(); i++){
cout<<d1[i]<<" "<<d2[i]<<endl;
} return 0;
}
Converting the DynamicArray class into a class template increased its
usefulness as it can now be used to hold different types of objects,
even user-defined types. The above example gives a main() function
showing the DynamicArray class template in use.
No comments:
Post a Comment