Templates
Function Templates
Function templates enable generic programming by allowing functions to operate with different data types.
#include <iostream>
using namespace std;
template<typename T>
void swapValues(T& a, T& b) {
T temp = a;
a = b;
b = temp;
}
void testFunctionTemplate() {
int x = 10, y = 20;
swapValues(x, y);
cout << "x = " << x << ", y = " << y << endl;
double a = 1.5, b = 2.5;
swapValues(a, b);
cout << "a = " << a << ", b = " << b << endl;
}
Class Tempaltes
Class templates provide a way to create generic classes that can work with various data types.
#include <string>
template<class NameType, class AgeType>
class Person {
public:
Person(NameType name, AgeType age) : mName(name), mAge(age) {}
void display() const {
cout << "Name: " << mName << ", Age: " << mAge << endl;
}
private:
NameType mName;
AgeType mAge;
};
void testClassTemplate() {
Person<string, int> person("Alice", 30);
person.display();
}
Standard Template Library
Container Overview
STL containers provide standardized ways to store and organize data:
- Sequence Containers: vector, deque, list
- Associative Containers: set, multiset, map, multimap
- Container Adapters: stack, queue
Vector Operations
#include <vector>
#include <algorithm>
void demonstrateVector() {
vector<int> numbers;
// Insert elements
numbers.push_back(10);
numbers.push_back(20);
numbers.push_back(30);
// Iterate through elements
for (auto it = numbers.begin(); it != numbers.end(); ++it) {
cout << *it << " ";
}
cout << endl;
// Sort elements
sort(numbers.begin(), numbers.end());
// Access elements
cout << "First element: " << numbers.front() << endl;
cout << "Last element: " << numbers.back() << endl;
}
String Manipulation
#include <string>
void demonstrateString() {
string text = "Hello";
// Concatenation
text += " World";
// Find substring
size_t pos = text.find("World");
if (pos != string::npos) {
cout << "Found at position: " << pos << endl;
}
// Extract substring
string sub = text.substr(6, 5);
cout << "Substring: " << sub << endl;
}
Algorithm Usage
STL algorithms provide powerful operations for containers:
#include <algorithm>
#include <vector>
void demonstrateAlgorithms() {
vector<int> data = {5, 2, 8, 1, 9};
// Find maximum element
auto maxIt = max_element(data.begin(), data.end());
cout << "Maximum: " << *maxIt << endl;
// Count occurrences
int count = count(data.begin(), data.end(), 5);
cout << "Count of 5: " << count << endl;
// Transform elements
transform(data.begin(), data.end(), data.begin(), [](int x) { return x * 2; });
}
Advnaced Container Features
Set Operations
#include <set>
void demonstrateSet() {
set<int> uniqueNumbers = {1, 2, 3, 4, 5};
// Insert elements
uniqueNumbers.insert(6);
// Check existence
if (uniqueNumbers.find(3) != uniqueNumbers.end()) {
cout << "Found 3" << endl;
}
// Iterate
for (const auto& num : uniqueNumbers) {
cout << num << " ";
}
cout << endl;
}
Map Usage
#include <map>
void demonstrateMap() {
map<string, int> scores;
scores["Alice"] = 95;
scores["Bob"] = 87;
// Access elements
cout << "Alice's score: " << scores["Alice"] << endl;
// Iterate through pairs
for (const auto& pair : scores) {
cout << pair.first << ": " << pair.second << endl;
}
}
List Operations
#include <list>
void demonstrateList() {
list<int> numbers = {1, 2, 3, 4, 5};
// Add elements
numbers.push_front(0);
numbers.push_back(6);
// Remove elements
numbers.pop_front();
numbers.pop_back();
// Sort
numbers.sort();
// Print all elements
for (const auto& num : numbers) {
cout << num << " ";
}
cout << endl;
}
Functional Programming Concepts
Function Objects
Function objects can maintain state and behave like functions:
#include <functional>
class Counter {
private:
int count;
public:
Counter() : count(0) {}
int operator()() {
return ++count;
}
};
void demonstrateFunctionObject() {
Counter counter;
cout << counter() << endl; // 1
cout << counter() << endl; // 2
cout << counter() << endl; // 3
}
Predicate Functions
Predicate functions return boolean values and are useful for filtering:
#include <algorithm>
bool isEven(int number) {
return number % 2 == 0;
}
void demonstratePredicate() {
vector<int> numbers = {1, 2, 3, 4, 5, 6, 7, 8};
// Count even numbers
int evenCount = count_if(numbers.begin(), numbers.end(), isEven);
cout << "Even numbers: " << evenCount << endl;
// Find first odd number
auto oddIt = find_if(numbers.begin(), numbers.end(), [](int n) { return n % 2 == 1; });
if (oddIt != numbers.end()) {
cout << "First odd: " << *oddIt << endl;
}
}
STL Algorithms
#include <numeric>
#include <algorithm>
void demonstrateNumericAlgorithms() {
vector<int> data = {1, 2, 3, 4, 5};
// Calculate sum
int sum = accumulate(data.begin(), data.end(), 0);
cout << "Sum: " << sum << endl;
// Fill container
vector<int> filled(5, 10); // 5 elements with value 10
// Generate sequence
iota(filled.begin(), filled.end(), 1); // 1, 2, 3, 4, 5
}
Practical Applications
Employee Management System
#include <map>
#include <vector>
#include <algorithm>
struct Employee {
string name;
int salary;
string department;
};
void processEmployees() {
vector<Employee> employees = {
{"John", 50000, "Engineering"},
{"Jane", 60000, "Marketing"},
{"Bob", 55000, "Engineering"}
};
// Group by department
map<string, vector<Employee>> grouped;
for (const auto& emp : employees) {
grouped[emp.department].push_back(emp);
}
// Display results
for (const auto& [dept, empList] : grouped) {
cout << dept << ":" << endl;
for (const auto& emp : empList) {
cout << " " << emp.name << " (" << emp.salary << ")" << endl;
}
}
}
Data Processing Pipeline
#include <functional>
#include <algorithm>
void processDataPipeline() {
vector<int> input = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
// Chain operations
vector<int> result;
// Filter even numbers
copy_if(input.begin(), input.end(), back_inserter(result),
[](int x) { return x % 2 == 0; });
// Square them
transform(result.begin(), result.end(), result.begin(),
[](int x) { return x * x; });
// Sort descending
sort(result.begin(), result.end(), greater<int>());
// Print results
for (const auto& val : result) {
cout << val << " ";
}
cout << endl;
}
Memory Management
Smart Pointers
#include <memory>
void demonstrateSmartPointers() {
// Unique pointer
unique_ptr<int> ptr(new int(42));
cout << *ptr << endl;
// Shared pointer
shared_ptr<string> sharedStr = make_shared<string>("Hello");
shared_ptr<string> anotherCopy = sharedStr;
cout << *sharedStr << " " << *anotherCopy << endl;
}
Container Optimization
void optimizeContainers() {
// Reserve space when size is known
vector<int> largeVector;
largeVector.reserve(1000);
// Use emplace instead of push_back for efficiency
vector<string> strings;
strings.emplace_back("Hello");
// Avoid unnecessary copies
const auto& ref = strings.front();
}
Performance Considerations
Iterator Best Practices
void efficientIteration() {
vector<int> data(1000000);
// Prefer range-based loops
for (const auto& item : data) {
// Process item
}
// Or use iterators efficiently
for (auto it = data.begin(); it != data.end(); ++it) {
// Process *it
}
}
Algorithm Selection
Choose appropriate algorithms based on container type:
- For vectors: use random access iterators
- For lists: prefer sequential operations
- For sets/maps: leverage logarithmic time complexity
Performance optimizations include:
- Using
reserve()before inserting many elements - Choosing appropriate container types
- Leveraging move semantics where possible
- Minimizing memory reallocations
These techniques help build efficient, maintainable C++ applications that take full advantage of modern standard library features.