Generic
Programming
Generic Programming
Generic Programming is the idea to allow type (Integer, String,
... etc and user-defined types) to be a parameter to methods,
classes and interfaces.
The method of Generic Programming is implemented to increase the
efficiency of the code.
Generic Programming enables the programmer to write a general
algorithm which will work with all data types.
Advantages
● Code Reusability
● Avoid Function Overloading
● Once written it can be used for multiple times and cases
Templates
Generics can be implemented in C++ using Templates.
Templates!
Function Templates
template <class T>
ret-type function-name(parameters)
{
// body of function
}
T is a placeholder that the compiler will automatically
replace with an actual data type.
Function Templates
#include <iostream>
using namespace std;
//template <typename T> // you can write any one of them
template <class T>
T findMax(T a, T b) {
return (a > b) ? a : b;
}
int main() {
int intA = 5, intB = 10;
cout << "Max integer value: " << findMax(intA, intB) << endl;
double doubleA = 3.5, doubleB = 7.2;
cout << "Max double value: " << findMax(doubleA, doubleB) << endl;
cout << "Max Char value: " << findMax('A', 'a') << endl;
return 0;
}
Class Activity
Write a generic function to swap two variables.
Template Function with Two Generic Types
You can define more than one generic data type in the
template statement by using a comma-separated list
Specialized Template int main() {
int intA = 5, intB = 10;
cout << "Max integer value: " <<
findMax(intA, intB) << endl;
double doubleA = 3.5, doubleB = 7.2;
#include <iostream> cout << "Max double value: " <<
using namespace std; findMax(doubleA, doubleB) << endl;
cout << "Max Char value: " << findMax('A',
template<class T> 'a') << endl;
T findMax(T a, T b) { }
return 0;
return (a > b) ? a : b;
}
template<>
int findMax(int a, int b) {
cout<<"I am template for int only"<<endl;
return (a > b) ? a : b;
}
Specialized Template int main() {
cout<<findMax<int>(10,5) <<endl;
cout<<findMax <char>('A', 'a') << endl;
return 0;
#include <iostream> }
using namespace std;
template<class T>
T findMax(T a, T b) {
return (a > b) ? a : b;
}
template<>
int findMax(int a, int b) {
cout<<"I am template for
int only"<<endl;
return (a > b) ? a : b;
}
Overloading a Generic Function
In addition to creating explicit, overloaded versions of a generic
function, you can also overload the template specification itself
To do so, simply create another version of the template that differs from
any others in its parameter list
Using Normal Parameters in Generic Functions
You can mix non-generic parameters with generic parameters in a template
function:
Generic Classes
The actual type of the data being used (in class) will
be specified as a parameter when objects of that class
are created.
Generic classes are useful when a class uses logic that
can be generalized e.g. Stacks, Queues
template <class T> class class-name
{
. . .
}
Generic Classes
If necessary, we can define more than one generic
data type using a comma-separated list
We create a specific instance of that class using
the following general form:
class-name <type> ob;
Generic Class
#include <iostream>
using namespace std;
template <class T1, class T2> int main(){
class myclass { myclass<int, double> ob1(10, 0.23);
T1 i;
myclass<char, char *> ob2('X',
T2 j;
public: "Hello");
myclass (T1 a, T2 b) { i = a; j = b; }
void show( ) { cout << i << “ & ” << j; } [Link](); // show int, double
T1 getmax();
[Link](); // show char, char *
};
}
template<class T1, class T2>
T1 myclass<T1, T2>::getmax() {
return i > j ? i : j;
}
Generic Classes
In a generic class, we can also specify non-type arguments:
template <class T, int size>
class MyClass
{
T arr[size]; // length of array is passed in size
// rest of the code in class
}
int main()
{
MyClass<int, 10> intob;
MyClass<double, 15> doubleob;
}
Generic BAse Classes & Derived Classes
template <class T>
class Base
{
};
template <class U, class T>
class Derived:public Base <T>
{};
class Derived:public Base <int>
{};
Generic Class
int main() {
Base<int> baseObj(5);
Derived<double> derivedObj(3.14);
[Link](); // Output: Base class: 5
#include <iostream> [Link](); // Output: Derived class:
using namespace std;
template<typename T> 3.14
class Base { return 0;
protected: }
T value;
public:
Base(const T& val) : value(val) {}
void display() {
cout << "Value in base class: " << value << endl;
}
};
template<typename T>
class Derived : public Base<T> {
public:
Derived(const T& val) : Base<T>(val) {}
void display() {
cout << "Value in derived class: " << this->value << endl;
}
};
Class Activity
Write a template class to manage an array of different data types
showing behaviour of stack. The class must have following
functions.
Push : when you push a variable onto the stack, it gets added to
the top of the stack.
Pop: when you pop an integer from the stack, you remove the top
integer from the stack.
Peek: when you peek at the stack, you can see the integer that is
currently at the top of the stack, but it remains on the stack.
Stack Class
#include <iostream>
using namespace std;
template<typename T>
class Stack {
private:
static const int MAX_SIZE = 100;
T elements[MAX_SIZE];
int topIndex;
public:
Stack() : topIndex(-1) {}
Stack Class
void push(const T& item) {
if (topIndex == MAX_SIZE - 1) {
cout << "Error: Stack is full" << endl;
return;
}
elements[++topIndex] = item;
}
Stack Class
T pop() {
if (topIndex == -1) {
cout << "Error: Stack is empty" << endl;
}
return elements[topIndex--];
}
Stack Class
T peek() const {
if (topIndex == -1) {
cout << "Error: Stack is empty" << endl;
}
return elements[topIndex];
}
Stack Class
int main() {
Stack<int> intStack;
[Link](10);
[Link](20);
[Link](30);
cout << "Top element: " << [Link]() << endl;
int popped = [Link]();
cout << "Popped element: " << popped << endl;
return 0;
}
Class Activity
Write a template class to manage an array of different data types
showing behaviour of queue. The class must have following functions.
Enqueue: the enqueue operation
adds an element to the back
(or end) of the queue.
Dequeue: the dequeue operation
removes and returns the element
at the front of the queue.
Front: the front function returns the element at the front of the
queue without removing it.
Queue Class
void enqueue(T value) {
if (count == SIZE) {
cout << "Queue is full\n";
return;
}
rearIndex = (rearIndex + 1) % SIZE;
arr[rearIndex] = value;
count++;
}
Queue Class
T dequeue() {
if (isEmpty()) {
cout << "Queue is empty\n";
return T(); // Return default value
}
T value = arr[frontIndex];
frontIndex = (frontIndex + 1) % SIZE;
count--;
return value;
}
Class Activity
Create a base class called Course that contains common properties
and methods for all courses. The class has attributes such as name,
course_code, credithours, and instructor. You define methods such
as print_details() which will be override in the derived class.
Next, you create several specific course classes that inherit from
the Course class. For example, you create a ThoeryCourse class that
has additional attributes such as projects and mid1 and mid2 and
final marks, and a LabCourse class that has attributes such as
lab_tasks and lab_mid and lab_final marks. Both classes have
get_grade() function which generates grades based on their
evaluation criteria.
Class Activity
Then, you create a generic function called display_grade()
that takes any Course object either TheoryCourse or LabCourse
as an argument and calls the get_grade() function.
Define a generic filter_courses() function to filter courses
by field value. It takes an array of courses, a second
parameter indicating the field to filter by (e.g.,
“instructor” or credit hours). The function should call print
detail functions for only those courses that match the
specified value.