Contents
Introduction
The STL ( Standard Template Library) is a set of C++ classes and functions that implement common data structures and algorithms. They are usually based on templates to allow generic use.
Containers
Containers contain collection of objects. We have several kinds such as vector, map, queue, list. Advantage of these over arrays is that they can grow and memory is taken care of internally.
File: v1.cpp
#include <iostream> #include <vector> using namespace std; int main() { // create a vector to store int vector<int> vec; int i1; // display the original size of vec cout << "vector size = " << vec.size() << endl; // push 5 values into the vector for(i1 = 0; i1 < 5; i1++) { vec.push_back(i); } // display extended size of vec cout << "extended vector size = " << vec.size() << endl; // access 5 values from the vector for(i1 = 0; i1 < 5; i1++) { cout << "value of vec [" << i1 << "] = " << vec[i1] << endl; } // use iterator to access the values vector<int>::iterator iter = vec.begin(); while( iter != vec.end()) { cout << "value of v = " << *iter << endl; iter++; } return 0; }
$ g++ v1.cpp ; ./a.exe vector size = 0 extended vector size = 5 value of vec [0] = 0 value of vec [1] = 1 value of vec [2] = 2 value of vec [3] = 3 value of vec [4] = 4 value of v = 0 value of v = 1 value of v = 2 value of v = 3 value of v = 4We use the "push_back" function to push the object to the end of the vector. The STL containers make copy of what we push to place in the container. If we look at the definition of the "push_back" call it looks like:
void push_back (const value_type& val);
Even though it seems to be taking a reference the STL eventually makes a copy somewhere stores the copy somewhere.
File: v2.cpp
#include <iostream> #include <vector> using namespace std; class student { public: int id ; student( int idP ) { id = idP ; cout << "Constructor called for ;" << id << endl ; } student( const student& obj1 ) { id = obj1.id ; cout << "Copy Constructor called for ;" << id << endl ; } ~student( ) { cout << "Destructor called for ;" << id << endl ; } }; int main() { // create a vector to store int vector<student> vec; int i1; student studentObj( 1 ) ; // display the original size of vec cout << "vector size = " << vec.size() << endl; vec.push_back( studentObj ); // display extended size of vec cout << "extended vector size = " << vec.size() << endl; cout << "value of vec [" << 0 << "] = " << vec[0].id << endl; return 0; }
$ g++ v2.cpp ; ./a.exe Constructor called for ;1 vector size = 0 Copy Constructor called for ;1 extended vector size = 1 value of vec [0] = 1 Destructor called for ;1 Destructor called for ;1If we do not want a copy then we can use pointers.
File: v3.cpp
#include <iostream> #include <vector> using namespace std; //Use pointers to avoid a copy class student { public: int id ; student( int idP ) { id = idP ; cout << "Constructor called for ;" << id << endl ; } student( const student& obj1 ) { id = obj1.id ; cout << "Copy Constructor called for ;" << id << endl ; } ~student( ) { cout << "Destructor called for ;" << id << endl ; } }; int main() { // create a vector to store int vector<student*> vec; int i1; //student studentObj( 1 ) ; student* ptr ; ptr = new student( 1 ) ; // display the original size of vec cout << "vector size = " << vec.size() << endl; // push 5 values into the vector vec.push_back( ptr ); // display extended size of vec cout << "extended vector size = " << vec.size() << endl; cout << "value of vec [" << 0 << "] = " << vec[0]->id << endl; delete ptr ; return 0; }This works but there are a couple of problems with this approach. We must remember to delete the pointer but only once and there is another copy in the vector object. If we delete the pointer first and then somewhere in the code the vector is used to access the object then the program does not work correctly.
File: v4.cpp
#include <iostream> #include <vector> using namespace std; //Use pointers to avoid a copy class student { public: int id ; student( int idP ) { id = idP ; cout << "Constructor called for ;" << id << endl ; } student( const student& obj1 ) { id = obj1.id ; cout << "Copy Constructor called for ;" << id << endl ; } ~student( ) { cout << "Destructor called for ;" << id << endl ; } }; int main() { // create a vector to store int vector<student*> vec; int i1; //student studentObj( 1 ) ; student* ptr ; ptr = new student( 1 ) ; // display the original size of vec cout << "vector size = " << vec.size() << endl; // push 5 values into the vector vec.push_back( ptr ); // display extended size of vec cout << "extended vector size = " << vec.size() << endl; // access 5 values from the vector cout << "value of vec [" << 0 << "] = " << vec[0]->id << endl; // use iterator to access the values delete ptr ; //Using the vector after the object has been deleted. cout << "value of vec [" << 0 << "] = " << vec[0]->id << endl; return 0; }
$ g++ v4.cpp ; ./a.exe Constructor called for ;1 vector size = 0 extended vector size = 1 value of vec [0] = 1 Destructor called for ;1 value of vec [0] = -631185240How do we take care of this problem. It's smart pointers to the rescue. A smart pointer is a wrapper around the raw pointer and takes care of certain details for us.
Smart Pointer
We have 2 different kinds of smart pointers with C++: unique pointer and shared pointer. A unique pointer only allows one variable and does not allow copying of that variable.File: smart1.cpp
#include <iostream> #include <memory> #include <utility> using namespace std; //unique_ptr class student { public: int id ; student( int idP ) { id = idP ; cout << "Constructor called for ;" << id << endl ; } student( const student& obj1 ) { id = obj1.id ; cout << "Copy Constructor called for ;" << id << endl ; } ~student( ) { cout << "Destructor called for ;" << id << endl ; } }; int main() { std::unique_ptr<student> valuePtr( new student(1) ) ; cout << "Student id:" << (*valuePtr).id << endl ; }The actual pointer is encapsulated in the object with the line:
std::unique_ptr
When the object "valuePtr: goes out of scope then it will call a delete on the pointer. We can use the "make_unique" function to help create the pointer also.
File: smart2.cpp
#include <iostream> #include <memory> #include <utility> using namespace std; //unique_ptr class student { public: int id ; student( int idP ) { id = idP ; cout << "Constructor called for ;" << id << endl ; } student( const student& obj1 ) { id = obj1.id ; cout << "Copy Constructor called for ;" << id << endl ; } ~student( ) { cout << "Destructor called for ;" << id << endl ; } }; void function1( unique_ptr<student> ptr ) { } int main() { std::unique_ptr<student> valuePtr( new student(1) ) ; cout << "Student id:" << (*valuePtr).id << endl ; //Compiler error . Cannot make //function1( valuePtr ) ; //Using the make_unique function std::unique_ptr<student> valuePtr1 = make_unique< student >(2) ; cout << "Student id:" << (*valuePtr1).id << endl ; }The "shared_ptr" allows us to make copies of the object. It keeps a referece count that is incremented every time the object is copied. That way the "delete" is only called once.
File: smart3.cpp
#include <iostream> #include <memory> #include <utility> using namespace std; //unique_ptr class student { public: int id ; student( int idP ) { id = idP ; cout << "Constructor called for ;" << id << endl ; } student( const student& obj1 ) { id = obj1.id ; cout << "Copy Constructor called for ;" << id << endl ; } ~student( ) { cout << "Destructor called for ;" << id << endl ; } }; void function1( shared_ptr<student> ptr ) { cout << "Inside function Reference count:" << ptr.use_count() << endl ; } int main() { std::shared_ptr<student> valuePtr( new student(1) ) ; cout << "Student id:" << (*valuePtr).id << endl ; // cout << "Before function Reference count:" << valuePtr.use_count() << endl ; function1( valuePtr ) ; cout << "Outside function Reference count:" << valuePtr.use_count() << endl ; //Using the make_unique function shared_ptr<student> valuePtr1 = make_shared< student >(2) ; cout << "Student id:" << (*valuePtr1).id << endl ; //Reference countins is used so that the destructor is //called only once. shared_ptr<student> valuePtr2 = valuePtr ; cout << "Reference count:" << valuePtr2.use_count() << endl ; function1( valuePtr ) ; }We can use the "shared_ptr" in our vector example.
File: v5.cpp
#include <iostream> #include <vector> #include <memory> using namespace std; //Use pointers to avoid a copy class student { public: int id ; student( int idP ) { id = idP ; cout << "Constructor called for ;" << id << endl ; } student( const student& obj1 ) { id = obj1.id ; cout << "Copy Constructor called for ;" << id << endl ; } ~student( ) { cout << "Destructor called for ;" << id << endl ; } }; int main() { // create a vector to store int vector< shared_ptr<student> > vec; { shared_ptr<student> valuePtr( new student(1) ) ; // display the original size of vec cout << "vector size = " << vec.size() << endl; // push 5 values into the vector vec.push_back( valuePtr ); //valuePtr gets destroyed here but //delete is not called for valuePtr } cout << "value of vec [" << 0 << "] = " << vec[0]->id << endl; return 0; }There is nothing special about the smart pointer. It is a class that someone wrote. We can write our own simple smart pointer.
File: smart5.cpp
#include <iostream> using namespace std ; class Person { int age; const char* pName; public: Person(): pName(0),age(0) { } Person(const char* pName, int age): pName(pName), age(age) { } ~Person() { cout << "Person Destructor." << endl ; } void Display() { printf("Name = %s Age = %d \n", pName, age); } }; template < typename T > class SP { private: T* pData; // Generic pointer to be stored public: SP(T* pValue) : pData(pValue) { } ~SP() { delete pData; } T& operator* () { return *pData; } T* operator-> () { return pData; } }; int main() { SP< Person > p(new Person("Scott", 25)); p->Display(); // Dont need to delete Person pointer.. return 0 ; }
List
File: list1.cpp
// C++ program to demonstrate the use of list containers #include <iostream> #include <list> using namespace std; int main() { // defining list list<int> gqlist{12,45,8,6}; //gqlist.insert( 1, 14, 1 ) ; for (auto i1 : gqlist) { cout << i1 << ' '; } //gqlist. list<int>::iterator it = gqlist.begin(); advance(it, 2) ; gqlist.insert( it, 1, 14 ) ; for (int i1 = 0; i1 < 2; ++i1 ) { gqlist.push_back( i1 * 2 ); gqlist.push_front( i1 * 3 ); } cout << endl ; for (auto i1 : gqlist) { cout << i1 << ' '; } return 0; }
Exercise
File: ex1.cpp
See the below links to help in the "showList" function. https://stackoverflow.com/questions/22874535/dependent-scope-need-typename-in-front
Solution
File: ex1s.cpp
Map
File: map1.cpp
File: map1.cpp
File: map1.cpp
Algorithms
This contains search, replace, sort algorithms.An example of the sort function.
File: sort1.cpp
File: sort2.cpp
An example of the replace function.
File: replace1.cpp