In previous studies, we delved into the mechanisms of C++ templates, including function templates and class templates. They are the cornerstone of generic programming—writing code independent of data types. The STL (Standard Template Library) is the most outstanding practice of the generic programming concept. It provides a series of generic, type-safe, high-performance containers, algorithms, and iterators that work perfectly together through template technology.
This article will focus on the three core components of the STL: the two most commonly used containers, <span>vector</span> and <span>map</span>, and a powerful algorithm <span>sort</span>, demonstrating how they can be used together through examples.
1. Sequence Container:<span>std::vector</span>
<span>std::vector</span> is a dynamic array and a typical representative of sequence containers. It can automatically manage memory, dynamically grow or shrink at runtime, and provides fast random access to elements.
1.1 Basic Usage
#include <iostream>
#include <vector> // Must include vector header
int main() {
// 1. Define a vector that stores int
std::vector<int> myVector;
// 2. Add elements to the end (dynamic resizing)
myVector.push_back(10);
myVector.push_back(20);
myVector.push_back(30);
// 3. Access elements using index like an array
std::cout << "First element: " << myVector[0] << std::endl; // Output 10
// 4. Use at() for access, which performs boundary checking, safer
std::cout << "Second element: " << myVector.at(1) << std::endl; // Output 20
// myVector.at(5); // If out of bounds, throws std::out_of_range exception
// 5. Get size and capacity
std::cout << "Size: " << myVector.size() << std::endl; // Current number of elements: 3
std::cout << "Capacity: " << myVector.capacity() << std::endl; // Current allocated capacity
// 6. Range for loop traversal
std::cout << "Vector elements: ";
for (int num : myVector) {
std::cout << num << " ";
}
std::cout << std::endl;
return 0;
}
1.2 The Power of Generics
<span>vector</span> is a class template, which means it can hold almost any type.
#include <vector>
#include <string>
// Store strings
std::vector<std::string> stringVec = {"Hello", "World", "from", "C++"};
// Store custom struct
struct Point {
double x, y;
};
std::vector<Point> pointsVec = {{1.0, 2.5}, {3.4, 5.6}};
// Even store another vector (2D array)
std::vector<std::vector<int>> matrix = {
{1, 2, 3},
{4, 5, 6},
{7, 8, 9}
};
2. Associative Container:<span>std::map</span>
<span>std::map</span> is an associative container that stores key-value pairs. Each key is unique, and the container automatically sorts based on the key’s value (default in ascending order). It provides fast lookup capabilities based on keys.
2.1 Basic Usage
#include <iostream>
#include <map> // Must include map header
#include <string>
int main() {
// 1. Define a map, key is string, value is int
// Can be used to store student names and scores
std::map<std::string, int> studentScores;
// 2. Insert key-value pairs
studentScores["Alice"] = 95; // Insert using subscript operator
studentScores["Bob"] = 87;
studentScores.insert(std::make_pair("Charlie", 92)); // Insert using insert function
// 3. Access elements (by key)
std::cout << "Alice's score: " << studentScores["Alice"] << std::endl;
// Note: Accessing a non-existent key with subscript will automatically create that key (value initialized)
std::cout << "David's score (new): " << studentScores["David"] << std::endl; // Output 0
// 4. Find elements (safe way)
auto it = studentScores.find("Bob");
if (it != studentScores.end()) { // Check if found
std::cout << "Found Bob, score: " << it->second << std::endl; // it->first is key, it->second is value
} else {
std::cout << "Bob not found." << std::endl;
}
// 5. Traverse map
std::cout << "\nAll students and scores:" << std::endl;
for (const auto& pair : studentScores) { // pair is a std::pair<const std::string, int>
std::cout << pair.first << ": " << pair.second << std::endl;
}
return 0;
}
3. Generic Algorithm:<span>std::sort</span>
The STL provides a large number of generic algorithms independent of containers, which operate on elements in containers through iterators.<span>std::sort</span> is one of the most commonly used algorithms for sorting sequences.
3.1 Sorting <span>vector</span>
#include <iostream>
#include <vector>
#include <algorithm> // Must include algorithm header
int main() {
std::vector<int> numbers = {5, 2, 8, 1, 9, 3};
// Use std::sort for default sorting (ascending)
// numbers.begin() and numbers.end() are iterators defining the sorting range
std::sort(numbers.begin(), numbers.end());
std::cout << "Sorted (ascending): ";
for (int num : numbers) {
std::cout << num << " ";
}
std::cout << std::endl;
// Use custom comparison function for descending sorting
// Here we use a Lambda expression
std::sort(numbers.begin(), numbers.end(), [](int a, int b) {
return a > b; // If a is greater than b, a should come first
});
std::cout << "Sorted (descending): ";
for (int num : numbers) {
std::cout << num << " ";
}
std::cout << std::endl;
return 0;
}
3.2 Combining <span>vector</span> and <span>sort</span> to Handle Complex Data
This is where the true power of the STL is demonstrated: containers, algorithms, and iterators work seamlessly together.
#include <iostream>
#include <vector>
#include <algorithm>
#include <string>
struct Product {
std::string name;
double price;
};
int main() {
std::vector<Product> products = {
{"Laptop", 999.99},
{"Mouse", 24.99},
{"Keyboard", 49.99},
{"Monitor", 299.99}
};
// Task: Sort by price from low to high
std::sort(products.begin(), products.end(), [](const Product& a, const Product& b) {
return a.price < b.price;
});
std::cout << "Products sorted by price (low to high):\n";
for (const auto& prod : products) {
std::cout << prod.name << ": $" << prod.price << std::endl;
}
return 0;
}
4. Comprehensive Case: Word Frequency Counter
Let’s use a comprehensive case to demonstrate the powerful combination of <span>vector</span>, <span>map</span>, and <span>sort</span>. This program counts the occurrences of each word in a text and outputs them in order of frequency from high to low.
#include <iostream>
#include <string>
#include <vector>
#include <map>
#include <algorithm>
#include <sstream>
#include <cctype>
// Helper function: convert string to lowercase
std::string toLower(const std::string& str) {
std::string result = str;
for (char& c : result) {
c = std::tolower(c);
}
return result;
}
int main() {
std::string text = "C++ is a powerful language. C++ is widely used for system programming. Power and performance are key in C++.";
std::map<std::string, int> wordCount;
// Use string stream to split text into words
std::istringstream iss(text);
std::string word;
while (iss >> word) {
// Simple cleanup of words (remove punctuation, convert to lowercase)
// Note: This is a simple example; real text processing is more complex
if (!std::isalpha(word.back())) {
word.pop_back(); // Remove trailing punctuation, e.g., '.'
}
word = toLower(word);
// Count
wordCount[word]++;
}
// To sort, we need to move elements (pairs) from map to vector
// Because std::sort needs to work with random access iterators, and map's iterators are not
std::vector<std::pair<std::string, int>> vec(wordCount.begin(), wordCount.end());
// Sort vector by word frequency in descending order
std::sort(vec.begin(), vec.end(),
[](const std::pair<std::string, int>& a, const std::pair<std::string, int>& b) {
return a.second > b.second;
});
// Output results
std::cout << "Word Frequency:\n";
for (const auto& entry : vec) {
std::cout << entry.first << ": " << entry.second << std::endl;
}
return 0;
}
/* Possible output:
Word Frequency:
c++: 3
is: 2
a: 1
powerful: 1
language: 1
widely: 1
...
*/
Conclusion
Through this chapter, we have seen how the STL maximizes the power of templates and generic programming:
- Containers (
<span>vector</span>,<span>map</span>): As class templates, they are responsible for storing and managing collections of data in a generic way. - Algorithms (
<span>sort</span>): As function templates, they are responsible for operating on data in containers (such as sorting, searching, modifying). They do not depend on specific container types, only on the iterator concept. - Iterators: As the glue between containers and algorithms, they provide a generic way to traverse container elements.
This design of “separating data structures and algorithms” is one of the core ideas of generic programming. It greatly enhances the reusability and flexibility of code. You can create containers for custom data types and immediately operate on them using the hundreds of algorithms already available in the STL without rewriting sorting, searching, and other logic. Mastering the STL is essential for becoming an efficient C++ programmer.