//vector:
// - mimics raw c++ array, but dynamically resizable
// - very efficient - no safety processing
// - operation usually have ready-to-understand names
// - if operation is not efficient - it is not "directly" supported (i.e. - no "nice" name),
// but there will be a way ...
#include <vector>
std::vector<int> v; // size=0
//initialize (grow)
v.push_back( 1 ); // ++size
v.push_back( 2 ); // ++size
v.push_back( 3 ); // ++size
...
v.push_back( 10 ); // ++size
//access
v[5]; // ok
v[15]; // possible crash, no bounds checking
v.at( 15 ); // throws
//v.push_front(); // too slow - linear, not implemented
// 0 1 2 3 4 5 6 7
// ^
v.insert( v.begin(), 0 ); // but there is a way
//typical error
std::vector<int> v; // size=0
v.push_back( 1 );
v.push_back( 2 );
v.push_back( 3 ); // indicies 0,1,2 are defined
v[3] = 4; // 4 is outside of valid array (note size is still 3)
// 0 1 2 3 4 5 - indices
// 0 1 2 4 - data
// occup ^ STL end
v.push_back( 5 ); // overwrites 4 at position 3 (++size, size is 4 now)
// 1 2 3 5
// ^ new end
// vector access
// read overwrite existing position grow (new)
// operator[] operator[] push_back/insert
//
// size=s
// capacity=c
// +----------------+
// |0 1 2 3 4 5 E |
// +----------------+
// +----------------+
// |0 1 2 3 4 5 6 7 | E size = capapcity
// +----------------+
// v.push_back(8); // reallocate - 1) new 2) copy values 3 delete old array
// +--------------------------------+
// |0 1 2 3 4 5 6 7 8 E free |
// +--------------------------------+
// 1 2 4 8 16 32 64 128
// resize( s2 );
// size=s2
// capacity=?? // depends
//
// size=s
// capacity=c
// reserve( c2 ); // will do nothing if c2 <= c
// size=s
// capacity=c2
//v.reserve ( 16 ); // does not change size
// +--------------------------------+
// |0 1 2 3 4 5 6 7 8 E free |
// +--------------------------------+
// +-----------------------------------------+
// |0 1 2 3 4 5 6 7 8 E free |
// +-----------------------------------------+
// 0 15
// v.resize( 16 ) // changes size
// +--------------------------------+
// |0 1 2 3 4 5 6 7 8 E free |
// +--------------------------------+
// +-----------------------------------------+
// |0 1 2 3 4 5 6 7 8 0 0 0 0 0 0 0 0 0 0 0 0| E
// +-----------------------------------------+
// 0 15
// growing by +10 (instead of *2)
if ( v.size() == v.capacity() ) { // out of free space
v.reserve( v.size() +10 ); // addd 10 un-initialized positions
}
v.push_back( i );
// if growing by addding, n^2 copies
// if growing by multiplying, 2*n copies
// see "vector reallocations cost" on course website (1 down from this link)
// Example 1:
std::vector<int> v;
for ( int i =0; i<1<<20;++i ) {
v.push_back( i ); // 20 reallocations at 2^i
}
// Example 2:
std::vector<int> v( 1<<20 ); // size= 1 mil (1 mil elements are created by default ctors)
// equivalent
std::vector<int> v();
v.resize( 1<<20 );
for ( int i =0; i<1<<20;++i ) {
//v.push_back( i ); // we already have elements
v[i] = i; // overwrite old values
}
// Example 3:
std::vector<int> v;
v.reserve( 1<<20 ); // no ctors called ! creates uninialized space
for ( int i=0; i<10; ++i ) {
v.push_back( i ); // uses placement new in the space allocated by reserve
}
// if we need one more
v.push_back( 11111111 ); // "automatic" reallocation
// another ctor
std::vector<int> v( 10, 1 );
// trimming capacity to size
std::vector<int>(cont1).swap(cont1);
// -----------------------
// unnamed temp( s=c=cont1.size()=50 )
// after swap
// cont1: s=c=50
// C++11 has shrink_to_fit that probably does the above
//important
// 1 2 3 5 6 7
// ^ ^
// b e
// range b,e [b, e) 1,2
//
// 1 2 3 5 6 7
// ^ ^
// b e
// v.begin() v.end()
// whole vector
// write an alg to add doubled value to all odd values in vector<int>
// 1 2 3 4 5
// 1 2 3 4 5 2 6 10
// C++03 style
std::vector<int>::iterator
it = v.begin(),
it_e = v.end();
for ( ; it!=it_e; ++it ) { // for( int* p=a; p!=p+size; ++p )
if ( *it %2 == 1 ) {
v.push_back( *it * 2 );
}
}
// C++11 style
for ( auto const& el : v ) {
if ( el%2 == 1 ) {
v.push_back( el * 2 );
}
}
//problem - iterator will be invalidated during one of the push_backs and next iteration of the loop will crash
// NEVER mix traversal and modifying (size)
// +--------------------------------------------------------------+
// +-----------+ |
// | +--------------------------------------------------------------+
// +---+----+ ^
// | | | iterator
// +--------+
//
// pushback => reallocate
// garbage
// +---+----+ ^
// | | | iterator
// +--------+
// | +--------------------------------------------------------------------------------------------+
// +-----------+ |
// +--------------------------------------------------------------------------------------------+
// separate the traversal and insertions
//traverse, place new elements into a separate container
std::vector<int>::iterator
it = v.begin(),
it_e = v.end();
std::vector<int> temp;
for ( ; it!=it_e; ++it ) {
if ( *it %2 == 1 ) {
temp.push_back( *it * 2 );
}
}
//add elements
v.insert( v.end(), temp.begin(), temp.end() ); // range insert
// reread iterators after modifying container !!!!
it = v.begin(),
it_e = v.end();
for ( ; it!=it_e; ++it ) {
std::cout << *it << " ";
}
//write an alg to delete all 2's from vector<int>
// exersize
// owning semantics - all STL containers
std::vector<C> v;
for( int i=0; i<100; ++i ) {
C c( i ); // client's object
v.push_back( c ); // copy to container
c = C(2); // does not change value in vector - STL OWNS data
}
// vector of pointers to objects (possibly base class pointers)
// vector owns pointers - not data. Now client is responsible for deletion
int main() {
std::vector<C*> v;
for( int i=0; i<100; ++i ) {
C* pc = new C( i );
v.push_back( pc );
}
std::vector<int>::iterator
it = v.begin(),
it_e = v.end();
for ( ; it!=it_e; ++it ) {
std::cout << **it << " ";
}
// container is responsible for pointers only, so objects have to be deleted manually
for ( ; it!=it_e; ++it ) {
delete *it;
}
}
//assume C::operator<() is defined
std::vector<C*> v;
for( int i=0; i<100; ++i ) {
C* pc = new C( rand()%1000 );
v.push_back( pc );
}
std::sort( v.begin(), v.end() ); // will sort addresses
//solution
bool sort_C( C const* p1, C const* p2 ) {
return *p1 < *p2;
}
std::sort( v.begin(), v.end(), sort_C );
// there is a better method - later
//print vector in reverse!!!
//
// ^ ^
// | |
// begin end
// ugly and error-prone
for ( it = it_e -1; it >= it_b; --it ) { ... }
// correct
//rend rbegin
// | |
// V V
// 1 2 3 4 5 6
for ( vector<int>::reverse_const_iterator rit = v.rbegin();
rit != v.rend();
++rit )
vector<int> const v; // constant vector
//vector<int>::iterator it = v.begin(); // error, read-write iterator to const memory
// int const i;
// int * p = &i;
for ( vector<int>::const_iterator it = v.begin();
it != v.end();
++it )
{
std::cout << *it << " ";
}
vector<int>::const_iterator it1;
++it;
// *it = 5;
// like: int const * p;
// note that
const vector<int>::iterator it1;
//++it1;
*it1 = 5;
// like: int * const p;
// using iterators - identical syntax for all STL containers
std::vector<std::string>::iterator it2;
for (it2 = cont2.begin(); it2 != cont2.end(); ++it2)
std::cout << *it << " ";
std::list<std::string>::iterator it;
for (it = cont1.begin(); it != cont1.end(); ++it)
std::cout << *it << " ";
//std::vector_iterator<int> iter;
typedef std::list<int> MyContainerT;
MyContainerT v;
MyContainerT::iterator iter;
// more ctors
std::vector<int> v1;
std::vector<int> v2( v1 );
//std::vector<double> v3( v1 );
std::vector<double> v3( v1.begin(), v1.end() );
class C {
public:
static const int i = 10;
static int foo() return 111; }
typedef double MT;
};
C::i
C::foo
C::MT
template <typename T>
void print4(T& v)
{
//T::iterator iter;
typename T::iterator iter;
for (iter = v.begin(); iter != v.end(); ++iter)
std::cout << *iter << " ";
std::cout << std::endl;
}
std::vector< std::vector <int> >
std::vector< std::vector <int>> // MS C++03 and C++11 are OK
struct C_pointer_comparison {
bool operator() ( C const * p1, C const * p2 ) const {
return *p1 < *p2;
}
};
C_pointer_comparison op;
op( p1, p2 );
#include <limits>
// 2-dimensional vector initialized to infinty
int n;
std::cin >> n;
std::vector< std::vector< int> >
v( n, std::vector< int>(
n, std::numeric_limits<int>::infinity() // infinity is C++11
)
);
// c++03
//m = std::numeric_limits<int>::max()
//m+1 is -1 ???
//std::numeric_limits<int>::min()
//// c++11
//std::numeric_limits<int>::infinity()
//m+1 is infinty (not imlemented in GNU 7)
//std::numeric_limits<double>::infinity()
//m+1 is infinty (IS imlemented in GNU 7)
std::deque<int> d;
d.push_back( 1 );
d.push_back( 2 );
d.push_back( 3 );
d.push_front( 4 );
d.push_front( 5 );
d.push_front( 6 );
6 5 4 1 2 3
0 1 2 3 4 5
// deque does not invalidate - false
-3 -2 -1
1 2 3 4 5 6 7
^
//d.reserve()
std::list<int> lst;
lst.push_back();
lst.push_front();
// lst[i]
1 2 3
lst.insert( lst.end(), 5 );
1 2 3 5
lst.insert( lst.begin(), 0 );
0 1 2 3 5
// lst.reserve() resize()