6 hours · 8 marks
Templates allow writing generic functions and classes that work with any data type, without duplicating code for each type — the compiler generates the type-specific version when the template is used.
template <class T> // or 'typename T'
T maxVal(T a, T b) {
return (a > b) ? a : b;
}
int main() {
cout << maxVal(3, 7); // T = int
cout << maxVal(3.5, 2.1); // T = double
cout << maxVal('a', 'z'); // T = char
}
The compiler deduces T from the arguments and generates a separate function for each distinct type actually used (a process called template instantiation).
A function template can be overloaded with another template (different parameter pattern) or with an ordinary (non-template) function of the same name; the compiler prefers the best/most specific match.
template <class T>
T maxVal(T a, T b) { return (a > b) ? a : b; }
template <class T>
T maxVal(T a, T b, T c) { return maxVal(maxVal(a,b), c); } // overloaded template, 3 args
int maxVal(int a, int b) { cout << "(non-template) "; return (a>b)?a:b; } // ordinary overload
template <class T>
class Box {
T value;
public:
Box(T v): value(v) {}
T getValue() { return value; }
};
int main() {
Box<int> b1(10);
Box<string> b2("hello");
cout << b1.getValue() << " " << b2.getValue();
}
A class template lets an entire class (with any number of members) be generic over one or more types, specified in angle brackets <> when the object is declared.
A template class can be used as a base class, and the derived class can itself be a template or an ordinary class instantiating the base with a specific type.
template <class T>
class Base {
protected:
T data;
public:
Base(T d): data(d) {}
};
template <class T>
class Derived : public Base<T> { // derived class template
public:
Derived(T d): Base<T>(d) {}
void show() { cout << this->data; }
};
// or a non-template derived class fixing the type:
class IntDerived : public Base<int> {
public:
IntDerived(int d): Base<int>(d) {}
};
The STL is a library of generic, reusable, template-based data structures and algorithms bundled with C++. It dramatically reduces the need to write common data-structure code from scratch.
vector, list, map).operator()), often used to customize algorithm behaviour.| Container | Description |
|---|---|
vector | Dynamic array, fast random access, efficient insertion/removal at the end. |
list | Doubly linked list, efficient insertion/removal anywhere, no random access. |
deque | Double-ended queue, fast insertion/removal at both ends. |
set / multiset | Sorted collection of unique / non-unique elements. |
map / multimap | Sorted collection of key-value pairs, unique / non-unique keys. |
stack, queue, priority_queue | Container adapters restricting access patterns (LIFO, FIFO, priority order). |
#include <vector>
vector<int> v;
v.push_back(10);
v.push_back(20);
for (int x : v) cout << x << " ";
Iterators provide a uniform way to traverse different containers, mimicking pointer syntax (*it, ++it).
vector<int> v = {1, 2, 3};
for (vector<int>::iterator it = v.begin(); it != v.end(); ++it)
cout << *it << " ";
Types include: input, output, forward, bidirectional, and random-access iterators, offering progressively more capability.
The <algorithm> header provides generic functions that work with any container via iterators: sort(), find(), reverse(), max_element(), count(), accumulate() (in <numeric>), etc.
#include <algorithm>
vector<int> v = {5, 2, 8, 1};
sort(v.begin(), v.end()); // ascending sort
auto it = find(v.begin(), v.end(), 8); // search for value 8
maxVal example in section 8.1.Stack with push() and pop() operations. 7 markstemplate <class T>
class Stack {
T arr[100];
int top = -1;
public:
void push(T val) { arr[++top] = val; }
T pop() { return arr[top--]; }
bool isEmpty() { return top == -1; }
};
int main() {
Stack<int> s;
s.push(10); s.push(20);
cout << s.pop(); // 20
}
Box<int>).vector and list containers in STL. 4 marksvector stores elements contiguously (like a dynamic array): fast O(1) random access via index, but insertion/removal in the middle is O(n). list is a doubly linked list: O(1) insertion/removal anywhere given an iterator, but no random access (must traverse, O(n) to reach an element).vector<int> with begin()/end().sort() algorithm. 4 marks#include <vector>
#include <algorithm>
#include <iostream>
using namespace std;
int main() {
vector<int> v = {5, 1, 4, 2, 3};
sort(v.begin(), v.end());
for (int x : v) cout << x << " "; // 1 2 3 4 5
}
Derived<T> : public Base<T>) or fixing a concrete type (IntDerived : public Base<int>). See section 8.4.