SEP200

The STL: Strings, Containers, and Iterators

Summary

The Standard Template Library

The String Class

Containers

Container Adaptors

Iterators

The Standard Template Library

Introduction

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.

Introduction

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

String Library

Introduction

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

Important Facts

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

String

Example

#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;
}

              

Containers

Introduction

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]

Introduction

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

Important Facts

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

Vector

Example

#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;
}

              

Deque

Example

#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;
}

              

Container Adapters

Introduction

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

Stack

Example

#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;
}
              

Queue

Example

#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

Introduction

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

Syntax and Usage

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

List

Example

#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;
}

              

Suggested Reading

STL and Strings

Containers and Iterators