Programming Workshop 2 (CSCI 1061U)
Faculty of Science, Ontario Tech University
Sequential containers are class that can be used for storing other items
std::vector<T>
std::list<T>
std::deque<T>
Allocating a vector of int
#include <vector>
std::vector<int> v;
Allocating a list of strings
#include <list>
#include <string>
std::list<std::string> s;
Allocating a deque of vectors of strings
#include <deque>
#include <vector>
#include <string>
std::deque<std::vector<std::string> >
The space between the last two >
in
std::deque<std::vector<std::string> >
is
important.
Container | Access | Addition | Deletion |
---|---|---|---|
vector |
O(1) | End | O(n) |
list |
O(n) | Anywhere | O(1) |
deque |
O(1) | Both ends | O(n) |
C++ STL containers (both sequential and associative) define “helper classes,” called iterators, to help iterate over each item in the container.
#include <iostream>
#include <vector>
int main()
{
std::vector<int> v;
.push_back(1);
v.push_back(2);
v.push_back(3);
v
std::vector<int>::iterator i;
for (i = v.begin(); i != v.end(); ++i)
{
std::cout << *i << std::endl;
}
return 0;
}
An iterator variable has the same semantics as a pointer to the
stored element. Dereference (use *
operator) to get the
actual value. Notice that we refer to the item as *i
, since
iterator i
stores a pointer to an element in the
container.
v.begin()
points to the first element in the sequence.
v.end()
points to the last element in the sequence.
#include <iostream>
#include <list>
using namespace std;
int main()
{
<string> names;
list.push_back("john");
names.push_back("amanda");
names
<string>::reverse_iterator i;
listfor (i = names.rbegin(); i != names.rend(); ++i)
{
<< *i << endl;
cout }
return 0;
}
Here names.rbegin()
refers to the last element in the
sequence. names.rend()
refers to the first element of the
sequence. ++i
essentiall decrements the iterator.
std::vector<int>::iterator
std::vector<int>::reverse_iterator
std::vector<int>::const_iterator
std::vector<int>::const_reverse_iterator
And the corresponding methods for initializing and checking iterators
begin()
end()
cbegin()
cend()
rbegin()
rend()
crbegin()
crend()
std::set<T>
<
operator Iterator is
availableSequential containers provide no meaningful way to index the
stored data. How do we index elements in vector
? The
elements are always indexed 0, 1, 2, …
Is it possible to index elements using labels, such as
age["earth"] = 4530000000
or
lastnames["John"]
.
STL provides std::map<K,V>
, which is an
associative container. Maps store (key,value) pairs. STL provides
std::pair<K,V>
for this purpose.
std::map
assumes that keys are unique, i.e., no two entries
can have the same key.
std::map
supports the following operations:
Iterators are available in std::map
.
#include <map>
#include <iostream>
#include <string>
using namespace std;
int main()
{
<string, int> super_heros;
map["batman"] = 32;
super_heros["wolverine"] = 137;
super_heros["jean gray"] = 25;
super_heros["superman"] = 35;
super_heros
<string, int>::iterator i;
mapfor (i = super_heros.begin(); i != super_heros.end(); ++i)
{
<< "Age of " << i->first << " is " << i->second << endl;
cout }
return 0;
}
Note that the items stores in std::map
of type
std::pair
. Each pair stores a key and a pair.
std::pair<string, int> ages;
.first = "John"; // Key
ages.second = 10; // Value ages
Maps are sometimes referred to as hashes or
dictinaires. Each key type should have a <
operator. This means that we can use a std::string
as a
key, but not a char
array. Value type should have default
constructor. Map provides []
operator for both insertion
and retrieval.
Associative container std::map
supports O(log n) look up
using the find
method as seen below.
#include <map>
#include <iostream>
#include <string>
using namespace std;
int main()
{
<string, int> super_heros;
map["batman"] = 32;
super_heros["wolverine"] = 137;
super_heros["jean gray"] = 25;
super_heros["superman"] = 35;
super_heros
<string, int>::iterator i = super_heros.find("batman");
mapif (i != super_heros.end()) {
<< "Batman is " << i->second << endl;
cout }
return 0;
}
find
returns an iterative to the (key,value) pair if the
key is found; otherwise, it returns an iterator equal to
end()
.
STL also provides std::multimap<K,V>
that allows
elements with repeating keys.
It is important to remember that elements have no particular ordering
in maps
or multimap
. This also means that
there isn’t an element 0.
These are template classes that are built “on top of” other classes.
std::stack
std::queue
std::priority_queue
Atapter classes have default underlying containers. E.g.,
std::stack
is implemented using std::deque
. It
is, however, possible to specify a different container as follows
std::stack<int, std::vector<int> >
Again, note the space between the last two >
.
STL also implements a number of algorithms that strive to be optimally efficient. Broadly speaking, we can divide these algorithms into two classes: 1) non-modifying sequence and 2) modifying sequence algorithms.
These templated functions do not modify the contents of the
container. An example is std::find()
, which returns the
iterator to the matching element or std::end()
if none is
found.
These templated funtions can change the contents of the container.
Adding or removing elements may invalidate an iterator.
std::list
guarantees that no iterator will be changed;
however, std::vector
and std::deque
provide no
such guarantees.
for
Starting c++11 the following works:
ranged-for.cpp
#include <iostream>
#include <string>
#include <map>
using namespace std;
int main()
{
<int, string> persons = ;
mapfor (auto i : persons) {
<< "id = " << i.first << " name = " << i.second << endl;
cout }
return 0;
}
You can compile this as follows:
> g++ -std=c++11 ranged-for.cpp