C++ PROGRAMMING:Module 10: STL (Standard Template Library)

Mastering module 10: stl (standard template library) concepts and implementation.

STL (Standard Template Library)

The Standard Template Library (STL) is a powerful collection of template classes and functions that provide common data structures and algorithms. It's one of C++'s greatest strengths.

What is STL?

STL consists of:

  • Containers: Data structures (vector, list, map, etc.)
  • Algorithms: Functions that operate on containers (sort, find, etc.)
  • Iterators: Objects that traverse containers
  • Function Objects: Objects that can be called like functions

Benefits:

  • Efficiency: Highly optimized implementations
  • Reusability: Pre-built, tested components
  • Type Safety: Templates ensure type safety
  • Standardization: Part of C++ standard

Containers

Sequence Containers

Store elements in linear order:

1. vector

Dynamic array that grows automatically:

#include <vector>
using namespace std;

vector<int> v;              // Empty vector
vector<int> v2(5);          // 5 elements (default initialized)
vector<int> v3(5, 10);      // 5 elements, all 10
vector<int> v4{1, 2, 3, 4}; // Initializer list

v.push_back(10);            // Add element
v.pop_back();               // Remove last element
v.size();                    // Number of elements
v.empty();                   // Check if empty
v[0];                        // Access element
v.at(0);                     // Safe access (throws exception)

2. list

Doubly linked list:

#include <list>
using namespace std;

list<int> l;
l.push_back(10);
l.push_front(5);
l.pop_back();
l.pop_front();

3. deque

Double-ended queue:

#include <deque>
using namespace std;

deque<int> dq;
dq.push_back(10);
dq.push_front(5);

Associative Containers

Store elements in sorted order:

4. set

Unique elements, sorted:

#include <set>
using namespace std;

set<int> s;
s.insert(10);
s.insert(5);
s.insert(10);  // Ignored (duplicate)
s.find(10);    // Returns iterator
s.count(10);    // 0 or 1

5. map

Key-value pairs, sorted by key:

#include <map>
using namespace std;

map<string, int> m;
m["Alice"] = 25;
m["Bob"] = 30;
m.insert({"Charlie", 28});
m["Alice"];                  // Access value
m.find("Bob");              // Returns iterator
m.count("Alice");           // 0 or 1

6. multiset / multimap

Allow duplicate keys:

multiset<int> ms;
ms.insert(10);
ms.insert(10);  // Allowed

Unordered Containers (C++11)

Hash-based containers (faster lookup):

7. unordered_set / unordered_map

#include <unordered_set>
#include <unordered_map>
using namespace std;

unordered_set<int> us;
unordered_map<string, int> um;

Iterators

Iterators provide a way to traverse containers:

Iterator Types

  • begin(): Points to first element
  • end(): Points past last element
  • rbegin(): Reverse begin
  • rend(): Reverse end

Iterator Categories

  1. Input Iterator: Read-only, forward
  2. Output Iterator: Write-only, forward
  3. Forward Iterator: Read/write, forward
  4. Bidirectional Iterator: Forward and backward
  5. Random Access Iterator: Jump to any position

Using Iterators

vector<int> v{1, 2, 3, 4, 5};

// Traditional loop
for (auto it = v.begin(); it != v.end(); ++it) {
    cout << *it << " ";
}

// Range-based for (C++11) - preferred
for (int num : v) {
    cout << num << " ";
}

// With auto
for (auto num : v) {
    cout << num << " ";
}

Algorithms

STL provides many algorithms in <algorithm>:

Sorting

#include <algorithm>
using namespace std;

vector<int> v{3, 1, 4, 1, 5};
sort(v.begin(), v.end());           // Ascending
sort(v.begin(), v.end(), greater<int>());  // Descending

Searching

vector<int> v{1, 2, 3, 4, 5};
auto it = find(v.begin(), v.end(), 3);
if (it != v.end()) {
    cout << "Found at position: " << distance(v.begin(), it) << endl;
}

// Binary search (requires sorted container)
bool found = binary_search(v.begin(), v.end(), 3);

Modifying Algorithms

vector<int> v{1, 2, 3, 4, 5};
reverse(v.begin(), v.end());
fill(v.begin(), v.end(), 0);
transform(v.begin(), v.end(), v.begin(), [](int x) { return x * 2; });

Counting

vector<int> v{1, 2, 2, 3, 2, 4};
int count = count(v.begin(), v.end(), 2);
int evenCount = count_if(v.begin(), v.end(), [](int x) { return x % 2 == 0; });

Common STL Operations

Vector Operations

vector<int> v;

// Adding elements
v.push_back(10);
v.insert(v.begin(), 5);
v.insert(v.begin() + 2, 7);

// Removing elements
v.pop_back();
v.erase(v.begin());
v.erase(v.begin(), v.begin() + 2);
v.clear();

// Accessing
v[0];           // No bounds checking
v.at(0);        // Bounds checking
v.front();      // First element
v.back();       // Last element

// Size
v.size();
v.empty();
v.capacity();   // Allocated space
v.reserve(100); // Reserve space

Map Operations

map<string, int> m;

// Inserting
m["Alice"] = 25;
m.insert({"Bob", 30});
m.insert(pair<string, int>("Charlie", 28));

// Accessing
int age = m["Alice"];  // Creates if doesn't exist
int age2 = m.at("Bob"); // Throws if doesn't exist

// Checking
if (m.find("Alice") != m.end()) {
    // Found
}

// Iterating
for (auto& pair : m) {
    cout << pair.first << ": " << pair.second << endl;
}

Function Objects (Functors)

Objects that can be called like functions:

class Add {
public:
    int operator()(int a, int b) {
        return a + b;
    }
};

Add add;
int result = add(5, 3);  // 8

Built-in Functors

#include <functional>
using namespace std;

plus<int> add;
minus<int> sub;
multiplies<int> mul;
divides<int> div;
modulus<int> mod;

greater<int> gt;
less<int> lt;
equal_to<int> eq;

Lambda Functions (C++11)

Anonymous function objects:

vector<int> v{1, 2, 3, 4, 5};

// Simple lambda
for_each(v.begin(), v.end(), [](int x) {
    cout << x << " ";
});

// Lambda with capture
int multiplier = 2;
transform(v.begin(), v.end(), v.begin(), [multiplier](int x) {
    return x * multiplier;
});

// Lambda with reference capture
int sum = 0;
for_each(v.begin(), v.end(), [&sum](int x) {
    sum += x;
});

Best Practices

  1. ✅ Prefer STL containers over raw arrays
  2. ✅ Use range-based for loops (C++11)
  3. ✅ Use algorithms instead of writing loops
  4. ✅ Choose right container for your needs
  5. ✅ Use const iterators when not modifying
  6. ✅ Reserve space for vectors when size known
  7. ✅ Use emplace_back (C++11) instead of push_back when possible
  8. ✅ Understand iterator invalidation rules

Container Selection Guide

NeedContainer
Dynamic array`vector`
Insert at front/back`deque`
Frequent insertions`list`
Sorted unique elements`set`
Key-value pairs`map`
Fast lookup`unordered_map`
Priority queue`priority_queue`

Common Mistakes

  • ❌ Using [] on map for non-existent key (creates it)
  • ❌ Iterator invalidation after container modification
  • ❌ Not checking iterator before dereferencing
  • ❌ Using wrong container for the use case
  • ❌ Not using algorithms when available
  • ❌ Forgetting to include necessary headers

Next Steps

You've now mastered the fundamentals of C++! Continue learning:

  • Templates: Generic programming
  • Smart Pointers: Modern memory management
  • Concurrency: Multithreading
  • File I/O: Reading and writing files
  • Exception Handling: Error management

Summary

STL provides:

  • Efficient, tested containers
  • Powerful algorithms
  • Type-safe templates
  • Standardized interfaces

Master STL, and you'll write efficient, maintainable C++ code!

Hands-on Examples

Vector Operations

#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;

int main() {
    // Vector declaration and initialization
    vector<int> v1;
    vector<int> v2(5, 10);  // 5 elements, all 10
    vector<int> v3{1, 2, 3, 4, 5};  // Initializer list
    
    // Adding elements
    v1.push_back(10);
    v1.push_back(20);
    v1.push_back(30);
    
    // Accessing elements
    cout << "v1[0]: " << v1[0] << endl;
    cout << "v1.at(1): " << v1.at(1) << endl;
    cout << "v1.front(): " << v1.front() << endl;
    cout << "v1.back(): " << v1.back() << endl;
    
    // Size
    cout << "Size: " << v1.size() << endl;
    cout << "Empty: " << (v1.empty() ? "Yes" : "No") << endl;
    
    // Iterating
    cout << "\nElements: ";
    for (int num : v1) {
        cout << num << " ";
    }
    cout << endl;
    
    // Using iterators
    cout << "Using iterators: ";
    for (auto it = v1.begin(); it != v1.end(); ++it) {
        cout << *it << " ";
    }
    cout << endl;
    
    // Modifying
    v1.insert(v1.begin() + 1, 15);
    v1.erase(v1.begin());
    
    cout << "\nAfter modifications: ";
    for (int num : v1) {
        cout << num << " ";
    }
    cout << endl;
    
    return 0;
}

Vector is a dynamic array that grows automatically. Use push_back to add elements, [] or at() to access. Iterators provide a way to traverse. insert() and erase() modify elements. Prefer range-based for loops for iteration.