The Standard Template Library
The String Class
Containers
Container Adaptors
Iterators
C++ has a small core that relies significantly on libraries for support
So far, we have used iostream
and cstring
extensively
There are also libraries for: types (including strings), containers, algorithms, multithreading, etc.
Most elements of this library are defined in the std
namespace
Today we will cover the string
library as well as libraries that implement containers and iterators
The string library implements a string class
This class allows for an easy storage and manipulation of strings of characters. It provides methods to:
→ Assign a sequence of characters into a string
→ Compare two strings
→ Get the size of a string
→ Convert a string into a char array
In C++, always use double quotes for strings and single quotes for characters
'q'
is a char, whereas "q"
is a string
There is no limit on the number of characters of a string. The library manages the memory for you
#include <iostream>
#include <string>
#include <fstream>
int main() {
std::string str1 = "Hello";
if (str1 == "Hello") {
std::cout << "str1 is Hello." << std::endl;
} else {
std::cout << "str1 is not Hello." << std::endl;
}
std::string str2;
str2 = "World";
std::string combined;
combined = str1 + " " + str2;
std::cout << "Concatenation: " << combined << std::endl;
std::cout << "Length: " << str1.size()
<< std::endl;
std::cout << "First character of str1: "
<< str1[0] << std::endl;
std::string part = combined.substr(3, 6);
std::cout << "Substring: " << part << std::endl;
size_t found = combined.find("World");
if (found != std::string::npos) {
std::cout << "'World' found at position: "
<< found << std::endl;
} else {
std::cout << "'World' not found in the string."
<< std::endl;
}
//example of string to char array conversion
std::string file = "example.txt";
std::ofstream file_stream;
file_stream.open(file.c_str());
file_stream << "Hello World!" << std::endl;
file_stream.close();
return 0;
}
So far, we have only used arrays as containers to store variables with multiple elements
Arrays have a fixed size:
→ determined at compilation time:
char array[const int]
→ determined at run time:
char *ptr = new array[int]
The STL provides a number of libraries to deal with different types of containers of variable size:
vector: contiguous storage, single-ended queue
deque: non-contiguous size, double-ended queue
list: non-contiguous size, doubly linked-list
forward_list: non-contiguous size, singly linked-list
Like string
, all these containers handle their own memory allocation
Different containers are better at different scenarios:
vector: allows fast random access
deque: inserts and remove elements at both ends
list: inserts elements at arbitrary positions
forward_list: less overhead than a list
#include <iostream>
#include <vector>
int main() {
std::vector<int> numbers;
numbers.push_back(10);
numbers.push_back(20);
numbers.push_back(30);
numbers.push_back(40);
numbers.push_back(50);
std::cout << "Elements in the vector:" << std::endl;
for (int i = 0; i < numbers.size(); ++i) {
std::cout << "Element at index " << i
<< " : " << numbers[i] << std::endl;
}
numbers[2] =25;
std::cout << "After modifying the third element:"
<< std::endl;
for (int i = 0; i < numbers.size(); ++i) {
std::cout << "Element at index " << i
<< " : " << numbers[i] << std::endl;
}
numbers.pop_back();
std::cout << "After removing the last element:"
<< std::endl;
for (size_t i = 0; i < numbers.size(); ++i) {
std::cout << "Element at index " << i
<< " : " << numbers[i] << std::endl;
}
return 0;
}
#include <iostream>
#include <deque>
int main() {
std::deque<int> dq;
dq.push_back(7);
dq.push_back(2);
dq.push_back(3);
dq.push_front(0);
dq.push_front(-1);
std::cout << "Elements in the deque: ";
for (int i = 0; i < dq.size(); i++) {
std::cout << dq[i] << " ";
}
std::cout << std::endl;
std::cout << "First element: " << dq.front()
<< std::endl;
std::cout << "Last element: " << dq.back()
<< std::endl;
dq.pop_front();
std::cout << "After pop_front, first element: "
<< dq.front() << std::endl;
dq.pop_back();
std::cout << "After pop_back, last element: "
<< dq.back() << std::endl;
std::cout << "Element 0: " << dq[1];
return 0;
}
The STL introduces classes that constrain how containers work
These adaptors are not containers themselves, but wrappers around other containers
The two most usefult adaptors are:
stack: deque in a last in, first out (LIFO) way
queue: deque in a first in, first out (FIFO) way
#include <iostream>
#include <stack>
int main() {
std::stack<int> st;
st.push(10);
st.push(20);
st.push(30);
std::cout << "Top element: " << st.top()
<< std::endl;
// error, stacks only allow access to top element
//std::cout << "element with index 1: << " st[1]
// << std::endl;
st.pop();
std::cout << "Top element after pop: " << st.top()
<< std::endl;
std::cout << "Elements in stack: ";
while (!st.empty()) {
std::cout << st.top() << " ";
st.pop();
}
std::cout << std::endl;
return 0;
}
#include <iostream>
#include <queue>
int main() {
std::queue<int> q;
q.push(10);
q.push(20);
q.push(30);
std::cout << "Front element: "
<< q.front() << std::endl;
std::cout << "Back element: "
<< q.back() << std::endl;
// error, queues only allow access to first element
//std::cout << "element with index 1: << " << q[1]
// << std::endl;
q.pop();
std::cout << "Front element after pop: "
<< q.front() << std::endl;
std::cout << "Back element after pop: "
<< q.back() << std::endl;
std::cout << "Elements in queue: ";
while (!q.empty()) {
std::cout << q.front() << " ";
q.pop();
}
std::cout << std::endl;
return 0;
}
Iterators are objects that point to a particular container item
Allow access to specific elements on non-contiguous containers
Provide entry points to insert or remove items
The syntax to create an iterator is:
std::container_type<type>::iterator iterator_name
Most containers have methods such as begin()
or end()
that return iterators
iterator_name++
moves it forward
iterator_name--
moves it back
*iterator_name
dereferences it
#include <iostream>
#include <list>
#include <iterator>
int main() {
std::list<int> l;
std::list<int>::iterator it;
l.push_back(20);
l.push_back(30);
l.push_front(10);
l.push_back(40);
l.push_back(50);
std::cout << "List elements using iterator: ";
for (it = l.begin(); it != l.end(); it++) {
std::cout << *it << " ";
}
std::cout << std::endl;
it = l.begin();
std::advance(it, 2);
l.erase(it);
std::cout << "List elements using iterator: ";
for (it = l.begin(); it != l.end(); it++) {
std::cout << *it << " ";
}
std::cout << std::endl;
l.pop_back();
l.pop_front();
std::cout << "List elements using iterator: ";
for (it = l.begin(); it != l.end(); it++) {
std::cout << *it << " ";
}
std::cout << std::endl;
return 0;
}