Playing with C++ STL

Playing With C++ Standard Template Library

#include <algorithm>    // for std::find
#include <iostream>     
#include <deque>        // for std::deque
#include <list>         // for std::list
#include <map>          // for std::map
#include <memory>       // for smart pointers
#include <string>       // for std::string
#include <utility>      // for std::pair
#include <vector>       // for std::vector

/*
 * In order for a class to be stored in an STL container, it must have:
 *
 * - a default constructor
 * - must be assignable
 * - less than comparable
 * - equality comparable
 *
 */
class aType {
    
    // all public here
    public:
    
    // a member
    std::string aString;
    
    // default constructor
    aType() {}
    
    // another constructor
    aType(std::string str) : aString(str) {}
    
    // copy constructor
    aType(const aType& t) {
        aString = t.aString;
    }
    
    // move constructor (takes an rvalue reference:
    // an rvalue is an unnamed value that exists
    // only during the evaluation of an expression)
    aType(aType&& t) {
        aString = t.aString;
    }
    
    // move operator (takes an rvalue reference:
    // an rvalue is an unnamed value that exists
    // only during the evaluation of an expression)
    aType& operator=(aType&& t) {
        aString = t.aString;
    }
    
    // assigment operator
    aType& operator=(const aType& t) {
        aString = t.aString;
    }
    
    // destructor
    virtual ~aType() {}
    
    // == operator
    int operator==(const aType& t) {
        return aString == t.aString;
    }
    
    // < operator
    int operator<(const aType& t) {
        return aString < t.aString;
    }
    
};

void playWithPair() {
    
    std::pair<std::string, int> aPair = make_pair(std::string("akiri"), 350);
    
    std::cout << "first is: " << aPair.first << std::endl;
    std::cout << "second is: " << aPair.second << std::endl;
    
}

void playWithVector() {
    
    /*
     * offers efficient insertion and removal at the end of the container,
     * also offers random access to its elements
     */
    
    // declare it
    std::vector<int> aVector;
    
    // fill some values
    for (int i=0; i<10; ++i) {
        aVector.push_back(i);
    }
    
    // iterate (forward)
    std::cout << "iterating forward" << std::endl;
    std::vector<int>::iterator it = aVector.begin();
    while(it != aVector.end()) {
        std::cout << *it << std::endl;
        ++it;
    }
    
    // iterate (backward)
    std::cout << "iterating backward" << std::endl;
    std::vector<int>::reverse_iterator rit = aVector.rbegin();
    while(rit != aVector.rend()) {
        std::cout << *rit << std::endl;
        ++rit;
    }
    
    // get size
    size_t size = aVector.size();
    std::cout << "size is: " << size << std::endl;
    
    // get capacity
    size_t capacity = aVector.capacity();
    std::cout << "capacity is: " << capacity << std::endl;
    
    // remove from back
    aVector.pop_back();
    
    // insert on top
    std::vector<int>::iterator start = aVector.begin();
    aVector.insert(start, 100);
    
    // insert somewhere
    std::vector<int>::iterator insertPosition = aVector.begin();
    insertPosition += 4;
    aVector.insert(insertPosition, 1234);
    
    // delete somewhere
    std::vector<int>::iterator erasePosition = aVector.begin();
    erasePosition += 3;
    aVector.erase(erasePosition);
    
    std::cout << "modified vector" << std::endl;
    std::vector<int>::iterator fit = aVector.begin();
    while(fit != aVector.end()) {
        std::cout << *fit << std::endl;
        ++fit;
    }
    
}

void playWithDeque() {
    
    /*
     * offers the same API as vector, but also
     * implements efficient insertion and removal
     * at the beginning of the sequence with:
     *
     * push_front()
     *
     * pop_front()
     *
     */
    
}

void playWithList() {
    
    /*
     * offers efficient inertion and removal anywhere, but does not
     * allow random access.
     *
     * std::list<int>::iterator it = std::find(aList.begin(), aList.end(), 1);
     */
     
    // declare it
    std::list<aType> aList;
    
    for (int i=0; i<10; ++i) {
        aList.push_front(
            aType(std::string("my value is: " + std::to_string(i)))
        );
    }
    
    std::list<aType>::iterator it = aList.begin();
    while(it != aList.end()) {
        std::cout << it->aString << std::endl;
        ++it;
    }
    
    // search an item (will find it)
    aType itemToFind(std::string("my value is: " + std::to_string(4)));
    std::list<aType>::iterator position = std::find(
        aList.begin(),
        aList.end(),
        itemToFind
    );
    
    if (position != aList.end()) {
        std::cout << "item found:" << std::endl;
        std::cout << position->aString << std::endl;
    }
    else {
        std::cout << "item not found" << std::endl;
    }
    
    // search an item (will not find it)
    aType iWontBeFound("I wont be found");
    std::list<aType>::iterator res = std::find(
        aList.begin(),
        aList.end(),
        iWontBeFound
    );
    
    if (res != aList.end()) {
        std::cout << "item found" << std::endl;
    }
    else {
        std::cout << "item not found" << std::endl;
    }
    
}

void playWithMap() {
    
    // declare it
    std::map<std::string, int> aMap;
    
    // fill some values
    std::pair<std::map<std::string, int>::iterator, bool> result =
    aMap.insert(make_pair(std::string("akiri"), 100));
    if (!result.second) {
        std::cout << "the value is already in the map!" << std::endl;
    }
    else {
        std::cout << "the value has been added to the map!" << std::endl;
    }
    std::cout << "the stored key is: " << result.first->first << std::endl;
    std::cout << "the stored value is: " << result.first->second << std::endl;
    
    aMap.insert(make_pair(std::string("teneb"), 150));
    aMap.insert(make_pair(std::string("erebos"), 200));
    aMap.insert(make_pair(std::string("narset"), 0));
    aMap["mayael"] = 250;
    aMap["ur-dragon"] = 300;
    aMap["brago"] = 0;
    
    // check insertion
    std::pair<std::map<std::string, int>::iterator, bool> checkInsert =
    aMap.insert(make_pair(std::string("akiri"), 100));
    if (!checkInsert.second) {
        std::cout << "the value is already in the map!" << std::endl;
    }
    else {
        std::cout << "the value has been added to the map!" << std::endl;
    }
    
    // iterate (forward)
    std::cout << "iterating forward" << std::endl;
    std::map<std::string, int>::iterator forwardIt = aMap.begin();
    while (forwardIt != aMap.end()) {
        std::cout << "aMap[" << forwardIt->first << "]=" << forwardIt->second << std::endl;
        ++forwardIt;
    }
    
    // iterate (backward)
    std::cout << "iterating backward" << std::endl;
    std::map<std::string, int>::reverse_iterator backwardIt = aMap.rbegin();
    while (backwardIt != aMap.rend()) {
        std::cout << "aMap[" << backwardIt->first << "]=" << backwardIt->second << std::endl;
        ++backwardIt;
    }
    
    // find a value
    std::map<std::string, int>::iterator res = aMap.find("teneb");
    if (res != aMap.end()) {
        std::cout << "found value:" << std::endl;
        std::cout << "aMap[" << res->first << "]=" << res->second << std::endl;
    }
    else {
        std::cout << "value not found:" << std::endl;
    }
    
    // erase a value by iterator
    std::cout << "erasing brago from the map" << std::endl;
    std::map<std::string, int>::iterator toErase = aMap.find("brago");
    aMap.erase(toErase);
    
    // erase a value by key
    std::cout << "erasing narset from the map" << std::endl;
    aMap.erase("narset");
    
    std::cout << "new map values" << std::endl;
    for (auto iter = aMap.begin(); iter != aMap.end(); ++iter) {
        std::cout << "aMap[" << iter->first << "]=" << iter->second << std::endl;
    }
    
}

void playWithSmartPointers() {
    
    // create a shared pointer
    std::shared_ptr<aType> aSharedPtr =
        std::make_shared<aType>("I am a shared object!");
        
    // this can be done
    std::shared_ptr<aType> anotherSharedPtr = aSharedPtr;
    
    // get the underlying raw pointer
    aType* rawPtr = aSharedPtr.get();
    
    // switch the pointed content with a new one
    // the previous object wont be deleted because anotherSharedPointer is
    // still holding it, but its reference count is decreased by 1
    aSharedPtr.reset(new aType("I am a new object"));
    
    // clear the content of anotherSharedPointer, now the old object is
    // deleted
    anotherSharedPtr.reset();
        
    // create a unique pointer
    std::unique_ptr<aType> aUniquePtr(new aType("I am a unique object!"));
    
    // this cannot be done: we get a compile error
    //std::unique_ptr<aType> anotherUniquePtr = aUniquePtr;
    
    // this can be done: transferring ownership
    std::unique_ptr<aType> yetAnotherUniquePtr(std::move(aUniquePtr));
    
}

int main() {
    
    std::cout << "Play with STL containers" << std::endl;
    
    playWithPair();
    
    playWithVector();
    
    playWithDeque();
    
    playWithList();
    
    playWithMap();
    
    playWithSmartPointers();

    return 0;
}
Written on February 21, 2018