Chapter 19 - Chapter 19 Chapter Vectors, templates, and...

Info iconThis preview shows page 1. Sign up to view the full content.

View Full Document Right Arrow Icon
This is the end of the preview. Sign up to access the rest of the document.

Unformatted text preview: Chapter 19 Chapter Vectors, templates, and exceptions Bjarne Stroustrup www.stroustrup.com/Programming Abstract Abstract This is the third of the lectures exploring the This design of the standard library vector and the techniques and language features used to implement it. Here, we deal with changing the size of a vector, parameter-ization of a vector with an element type (templates), and range checking (exceptions). checking Stroustrup/Programming 2 Overview Overview Vector revisited Pointers and free store Destructors Copy constructor and copy assignment Arrays Array and pointer problems Changing size How are they implemented? resize() and push_back() Templates Range checking and exceptions Stroustrup/Programming 3 Changing vector size Changing Fundamental problem addressed We (humans) want abstractions that can change size ( e.g., a vector vector where we can change the number of elements). However, in computer memory everything must have a fixed size, so how do we create the illusion of change? Given vector v(n); // v.size()==n // v.size()==n we can change its size in three ways Resize it // v now has 10 elements // 10 Add an element v.resize(10); v.push_back(7); // add an element with the value 7 to the end of v add // v.size() increases by 1 // increases Assign to it v = v2; // v is now a copy of v2 // is // v.size() now equals v2.size() // now Stroustrup/Programming 4 Representing vector Representing If you resize() or push_back() once, you’ll probably do it again; once, let’s prepare for that by sometimes keeping a bit of free space for future expansion class vector { int sz; double* elem; int space; // number of elements plus “free space” // number // (the number of “slots” for new elements) // (the public: // … // }; 0 sz: allocation: ------------elements------------free space-------------- Stroustrup/Programming 5 (initialized) (uninitialized) Representing vector Representing An empty vector (no free store use): A vector(N) (no free space): 0 N: Stroustrup/Programming 6 vector::reserve() vector::reserve() First deal with space (allocation); given space all else is easy Note: reserve() doesn’t mess with size or element values Note: doesn’t void vector::reserve(int newalloc) // make the vector have space for newalloc elements // make elements { if (newalloc<=space) return; // never decrease allocation // never double* p = new double[newalloc]; // allocate new space // allocate for (int i=0; i<sz; ++i) p[i]=elem[i]; // copy old elements // copy delete[ ] elem; // deallocate old space // deallocate elem = p; space = newalloc; } Stroustrup/Programming 7 vector::resize() vector::resize() Given reserve(), resize() is easy Given reserve() is reserve() deals with space/allocation reserve() deals resize() deals with element values resize() deals void vector::resize(int newsize) // make the vector have newsize elements // make elements // initialize each new element with the default value 0.0 { reserve(newsize); // make sure we have sufficient space // make for(int i = sz; i<newsize; ++i) elem[i] = 0; // initialize new elements // initialize sz = newsize; } Stroustrup/Programming 8 vector::push_back() vector::push_back() Given reserve(), push_back() is easy Given reserve() is reserve() deals with space/allocation reserve() deals push_back() just adds a value push_back() just void vector::push_back(double d) // increase vector size by one // increase vector by // initialize the new element with d // initialize { if (sz==0) // no space: grab some if no reserve(8); else if (sz==space) // no more free space: get more space else no get reserve(2*space); elem[sz] = d; // add d at end // ++sz; // and increase the size (sz is the number of elements) // and sz is } Stroustrup/Programming 9 resize() and push_back() resize() class vector { // an almost real vector of doubles // an double int sz; // the size // the double* elem; // a pointer to the elements // pointer int space; // size+free_space // size+free_space public: vector() : sz(0), elem(0), space(0) { } // default constructor // default constructor explicit vector(int s) :sz(s), elem(new double[s]) , space(s) { } // constructor // constructor vector(const vector&); // copy constructor copy vector& operator=(const vector&); // copy assignment copy ~vector() { delete[ ] elem; } // destructor destructor double& operator[ ](int n) { return elem[n]; } double& int size() const { return sz; } void resize(int newsize); void push_back(double d); // grow (or shrink) // grow // add element // add void reserve(int newalloc); int capacity() const { return space; } }; // access: return reference access: // current size // current // get more space // get // current available space // current Stroustrup/Programming 10 The this pointer this A vector is an object vector v(10); vector* p = &v; // we can point to a vector object vector Sometimes, vector’s member functions need to refer to that object Sometimes, vector The name of that “pointer to self” in a member function is this The this p: v: 10 10 this: 0.0 0.0 0.0 0.0 0.0 Stroustrup/Programming 0.0 0.0 0.0 0.0 0.0 11 The this pointer this vector& vector::operator=(const vector& a) // like copy constructor, but we must deal with old elements // like { // … // return *this; // by convention, // by // assignment returns a reference to its object: *this // assignment } void f(vector v1, vector v2, vector v3) { // … // v1 = v2 = v3; // rare use made possible by operator=() returning *this // operator=() *this // … // } The this pointer has uses that are less obscure The this one of which we’ll get to in two minutes Stroustrup/Programming 12 Assignment Assignment Copy and swap is a powerful general idea vector& vector::operator=(const vector& a) // like copy constructor, but we must deal with old elements // like // make a copy of a then replace the current sz and elem with a’s // sz elem { double* p = new double[a.sz]; // allocate new space // allocate for (int i = 0; i<a.sz; ++i) p[i] = a.elem[i]; // copy elements // copy delete[ ] elem; // deallocate old space // deallocate sz = a.sz; // set new size // set elem = p; // set new elements // set return *this; // return a self-reference return // return } Stroustrup/Programming 13 Optimize assignment Optimize “Copy and swap” is the most general idea but not always the most efficient What if there already is sufficient space in the target vector? Then just copy! For example: a = b; For b; b: sz: a: sz: Stroustrup/Programming 14 Optimized assignment Optimized vector& vector::operator=(const vector& a) { if (this==&a) return *this; // self-assignment, no work needed // self-assignment, if (a.sz<=space) { // enough space, no need for new allocation // enough no for (int i = 0; i<a.sz; ++i) elem[i] = a.elem[i]; // copy elements // copy space += sz-a.sz; // increase free space // increase sz = a.sz; return *this; } double* p = new double[a.sz]; for (int i = 0; i<a.sz; ++i) p[i] = a.elem[i]; delete[ ] elem; sz = a.sz; space = a.sz; elem = p; return *this; return } Stroustrup/Programming // copy and swap // copy 15 Templates Templates But we don’t just want vector of double We want vectors with element types we specify vector<double> vector<int> vector<Month> vector<Record*> vector< vector<Record> > vector<char> // vector of pointers // vector // vector of vectors // vector We must make the element type a parameter to vector We vector vector must be able to take both built-in types and uservector must defined types as element types This is not some magic reserved for the compiler, we can This define our own parameterized types, called “templates” define Stroustrup/Programming 16 Templates Templates The basis for generic programming in C++ Sometimes called “parametric polymorphism” Unsurpassed flexibility and performance Parameterization of types (and functions) by types (and integers) Used where performance is essential (e.g., hard real time and numerics) Used where flexibility is essential (e.g., the C++ standard library) Template definitions template<class T, int N> class Buffer { /* … */ }; template<class */ template<class T, int N> void fill(Buffer<T,N>& b) { /* … */ } template<class */ Template specializations (instantiations) // for a class template, you specify the template arguments: // for Buffer<char,1024> buf; // for buf, T is char and N is 1024 // for and is // for a function template, the compiler deduces the template arguments: // for fill(buf); // for fill(), T is char and N is 1024; that’s what buf has // for and is has Stroustrup/Programming 17 Parameterize with element type Parameterize // an almost real vector of Ts: // an template<class T> class vector { // … // }; vector<double> vd; vector<int> vi; vector< vector<int> > vvi; // T is double // double // T is int // is // T is vector<int> // vector<int> // in which T is int // in is vector<char> vc; // T is char // vector<double*> vpd; // T is double* // double* vector< vector<double>* > vvpd; // T is vector<double>* // is // in which T is double // in double Stroustrup/Programming 18 Basically, vector<double> is vector<double> // an almost real vector of doubles: // an double class vector { int sz; // the size // the double* elem; // a pointer to the elements // pointer int space; // size+free_space // size+free_space public: vector() : sz(0), elem(0), space(0) { } // default constructor // default constructor explicit vector(int s) :sz(s), elem(new double[s]), space(s) { } // constructor explicit constructor vector(const vector&); // copy constructor copy vector& operator=(const vector&); // copy assignment copy ~vector() { delete[ ] elem; } // destructor destructor double& operator[ ] (int n) { return elem[n]; } // access: return reference // access: int size() const { return sz; } // the current size // the // … }; Stroustrup/Programming 19 Basically, vector<char> is vector<char> // an almost real vector of chars: // an char class vector { int sz; // the size // the char* elem; // a pointer to the elements // pointer int space; // size+free_space // size+free_space public: vector() : sz(0), elem(0), space(0) { } // default constructor // default constructor explicit vector(int s) :sz(s), elem(new char[s]), space(s) { } // constructor explicit constructor vector(const vector&); // copy constructor copy vector& operator=(const vector&); // copy assignment copy ~vector() { delete[ ] elem; } // destructor destructor char& operator[ ] (int n) { return elem[n]; } int size() const { return sz; } // access: return reference // access: // the current size // the // … // }; Stroustrup/Programming 20 Basically, vector<T> is vector<T> // an almost real vector of Ts: // an template<class T> class vector { // read “for all types T” (just like in math) // read int sz; // the size // the T* elem; // a pointer to the elements // pointer int space; // size+free_space // size+free_space public: vector() : sz(0), elem(0), space(0); // default constructor // default explicit vector(int s) :sz(s), elem(new T[s]), space(s) { } // constructor constructor vector(const vector&); // copy constructor copy vector& operator=(const vector&); // copy assignment copy ~vector() { delete[ ] elem; } // destructor destructor T& operator[ ] (int n) { return elem[n]; } T& int size() const { return sz; } // access: return reference access: // the current size // the // … // }; Stroustrup/Programming 21 Templates Templates Problems (“there’s no free lunch”) Poor error diagnostics Delayed error messages Often at link time All templates must be fully defined in each translation unit Often spectacularly poor (the facility for separate compilation of templates, called “export”, is not widely available) So place template definitions in header files Recommendation Use template-based libraries Such as the C++ standard library E.g., vector, sort() vector sort() Soon to be described in some detail Initially, write only very simple templates yourself Until you get more experience Stroustrup/Programming 22 Range checking Range // an almost real vector of Ts: // an struct out_of_range { /* … */ }; struct template<class T> class vector { // … // T& operator[ ](int n); // access // access // … // }; }; template<class T> T& vector<T>::operator[ ](int n) T& { if (n<0 || sz<=n) throw out_of_range(); return elem[n]; } Stroustrup/Programming 23 Range checking Range void fill_vec(vector<int>& v, int n) // initialize v with factorials // with { for (int i=0; i<n; ++i) v.push_back(factorial(i)); } int main() { vector<int> v; try { fill_vec(v,10); for (int i=0; i<=v.size(); ++i) cout << "v[" << i << "]==" << v[i] << '\n'; } catch (out_of_range) { // we’ll get here (why?) // we’ll cout << "out of range error"; return 1; } Stroustrup/Programming } 24 Exception handling (primitive) (primitive) // sometimes we cannot do a complete cleanup // sometimes vector<int>* some_function() // make a filled vector // make { vector<int>* p = new vector<int>; // we allocate on free store, // we // someone must deallocate // someone try { fill_vec(*p,10); // … // return p; // all’s well; return the filled vector // all’s } catch (…) { delete p; // do our local cleanup // do throw; // re-throw to allow our caller to deal // } } Stroustrup/Programming 25 Exception handling (simpler and more structured) (simpler // When we use scoped variables cleanup is automatic // When vector<int> glob; void some_other_function() // make a filled vector // make { vector<int> v; // note: vector handles the deallocation of elements // note: fill_vec(v,10); // use v // use fill_vec(glob,10); // … // } if you feel that you need a try-block: think. You might be able to do without it Stroustrup/Programming 26 RAII (Resource Acquisition Is Initialization) (Resource Vector acquires memory for elements in its constructor Manage it (changing size, controlling access, etc.) Gives back (releases) the memory in the destructor This is a special case of the general resource management This strategy called RAII strategy Also called “scoped resource management” Use it wherever you can It is simpler and cheaper than anything else It interacts beautifully with error handling using exceptions Examples of “resources”: Memory, file handles, sockets, I/O connections (iostreams handle those using RAII), Memory, locks, widgets, threads. locks, Stroustrup/Programming 27 A confession confession The standard library vector doesn’t guarantee range checking of vector The You have been using Either our debug version, called Vector, which does check Vector Or a standard library version that does check (several do) Unless your version of the standard library checks, we “cheated” In std_lib_facilities.h, we use the nasty trick (a macro substitution) of In std_lib_facilities.h we redefining vector to mean Vector vector Vector #define vector Vector (This trick is nasty because what you see looking at the code is not This what compiler sees – in real code macros are a significant source of obscure errors) obscure We did the same for string We string Stroustrup/Programming 28 What the standard guarantees What // the standard library vector doesn’t guarantee a range check in operator[ ]: // operator[ template<class T> class vector { // … T& at(int n); // checked access checked T& operator[ ](int n); // unchecked access // unchecked }; }; template<class T> T& vector<T>::at (int n) T& { if (n<0 || sz<=n) throw out_of_range(); return elem[n]; } template<class T> T& vector<T>::operator[ ](int n) template<class T& { return elem[n]; Stroustrup/Programming } 29 What the standard guarantees What Why doesn’t the standard guarantee checking? Checking cost in speed and code size Not much; don’t worry Some projects need optimal performance No student project needs to worry Few real-world projects need to worry Think huge (e.g., Google) and tiny (e.g., cell phone) The standard must serve everybody You can build checked on top of optimal You can’t build optimal on top of checked Some projects are not allowed to use exceptions Old projects with pre-exception parts High reliability, hard-real-time code (think airplanes) Stroustrup/Programming 30 Access to const vectors Access template<class T> class vector { // … T& at(int n); const T& at(int n) const; T& operator[ ](int n); const T& operator[ ](int n) const; // … // // checked access checked // checked access checked // unchecked access // unchecked // unchecked access // unchecked }; }; void f(const vector<double> cvd, vector<double> vd) void { // … // double d1 = cvd[7]; // call the const version of [ ] // call version double d2 = vd[7]; // call the non-const version of [ ] call cvd[7] = 9; // error: call the const version of [ ] // error: version vd[7] = 9; // call the non-const version of [ ]: ok // call non-const version } Stroustrup/Programming 31 String String A string is rather similar to a vector<char> string E.g. size(), [ ], push_back() E.g. size(), Built with the same language features and techniques A string is optimized for character string manipulation string Concatenation (+) Can produce C-style string (c_str()) >> input terminated by whitespace 6 H Stroustrup/Programming o w d y ! \0 32 Next lecture Next An introduction to the STL, the containers and An algorithms part of the C++ standard library. Here we’ll meet sequences, iterators, and containers (such as vector, list, and map). The vector, and map). algorithms include find(), find_if(), sort(), find() copy(), copy_if(), and accumulate(). copy() and accumulate() Stroustrup/Programming 33 ...
View Full Document

This note was uploaded on 02/18/2012 for the course CSCE 121 taught by Professor Walter daugherity during the Fall '09 term at Texas A&M.

Ask a homework question - tutors are online