680 likes | 802 Vues
CS3101-2 Programming Languages – C++ Lecture 5. Matthew P. Johnson Columbia University Fall 2003. Agenda. hw3 was due last night Today: Templates Exceptions Other odds and ends The STL Grading and the final hw4 TBA tonight. Templates.
E N D
CS3101-2Programming Languages – C++Lecture 5 Matthew P. Johnson Columbia University Fall 2003 CS3101-2, Lecture 5
Agenda • hw3 was due last night • Today: • Templates • Exceptions • Other odds and ends • The STL • Grading and the final • hw4 TBA tonight CS3101-2, Lecture 5
Templates • Often want to do basically the same thing with different things • functions work on variables • only types specified • algorithmic thinking • computer science • “functionalism” in phil. of mind • abstraction • human = “the rational animal” (Aristotle) • Sometimes want to do basically the same thing with different types of things • Queue of ints, queue of widgets • “abstract data types” CS3101-2, Lecture 5
max functions • Suppose want the max of two numbers • What kind of numbers? • ints • chars • floats • doubles • All! • How? CS3101-2, Lecture 5
max functions • Soln 1: Write one, maxly general function • double max(double a, double b) { return a > b ? a : b; } • double x = max(2.5, 3.5); • char c = (char)max(‘A’,’B’); • This works but it’s not nice • All four types can widen to doubles • but must be cast back CS3101-2, Lecture 5
max functions • Soln 2: Write one function for each type • int max(int a, int b) { return a > b ? a : b; } • double max( … • etc. • Is allowed in C++ (though not in C) • But manually duplicating code • for nontrivial ftns – bad • hard to maintain CS3101-2, Lecture 5
max functions • Soln 3: Use the C preprocessor macros • #define max(a,b) (a > b ? a : b) • C source code is preprocessed • #includes replaced with header files • #ifndef, etc. • macro calls replaced with macro content • int c = max(2,3); • int c = (2 > 3 ? 2 : 3); • Works too, but complications, e.g.: • z = max(x++, y++) z = (x++ > y++ ? x++ : y++) • x, y inc-ed twice • Need many parens – sq(a+b), etc. CS3101-2, Lecture 5
max functions • Soln 4: Use the CPP in a more sophisticated way • Don’t use the CPP to generate expressions but to generate functions • #define define_max(t) \ t max(t a, t b) { \ return a > b ? a : b;\ } • define_max(char) • define_max(int) etc. – no ; • Avoids prev. CPP problems • But reqs code for all poss types • Done manually CS3101-2, Lecture 5
Templates • template <class T> result ftn(param-list) {…} • The place-holder for the substituted type is t • template and class are used as keywords • can use typename in place of class • T is a type • Primitive or class • All occurrences in ftn replaced with real type CS3101-2, Lecture 5
max functions • Soln 5: use templates • parameterized function • expands per type as necessary • template<typename T> T max(T a, T b) { return a > b ? a : b; } • Now can simply call the ftn: • x = max(2.5,3.5); • Compiler autoly creates only the ftn specializations needed CS3101-2, Lecture 5
Sorting things • Consider problem of sorting: • Sorting ints • Sorting doubles • Sorting strings • Sorting widgets • Point of sorting: put list in order • Q: What does “in order” mean? • A: Given an ordering relation < on the members • For x and y, tells whether x < y • Reorder s.t. x is before y iff x < y CS3101-2, Lecture 5
Generic sorting • Sort alg doesn’t depend of element type • Merge sort, quick sort, etc. • Need only give means to compare to elms • How? • In C we pass in a pointer to a compare ftn: • void qsort(void *base, int n, int size, int (*cmp)(const void *, void *)); • Pass in pointer to ftn: • int cmp(const void *a, void *b) { Widget* w1 = (Widget*)a; … } • Works, but very awkward CS3101-2, Lecture 5
Generic sorting • In Java, we pass a Comparable implementer • In the sort ftn we say • if (a.compareTo(b) < 0) … // means a < b • Objects must implement this interface • compare with ftn call • Primitives can’t implement • Compared with ops • could put in wrappers… CS3101-2, Lecture 5
Generic sorting • C++ soln 1: Define our own Comparable analog: abstract class • Has virtual compareTo • Or, better: has virtual < == > operators • Any class extending our class can now be sorted • Pass in array of Comparable-extending objects • Sort uses polymorphism to treat as (mere) Comparables • Downside: can only sort objects if they extend Comparable • Mult inher: can always add Comp parent, but must do so • To sort primitives • must create wrapper classes CS3101-2, Lecture 5
Generic sorting • C++ soln 2: use templates! • Let sort take an array of some arb. kind • Don’t need Comparable • Don’t need compareTo • In sort, just say • if (a < b) … • If these are numbers, this works • If these are objects that overload ops, this works • Only requirement: • kind supports < == > ops CS3101-2, Lecture 5
Templates: swapping • Remember our swap-with-ptrs ftn? • void swap(int &a, int &b) { int temp c = a; a = b; b = a; } • Suppose we want to swap other types • templates CS3101-2, Lecture 5
Generic swapping • template <class T>void swap(T &a, T &b) { T temp c = a; a = b; b = a; } • Now can swap any prim • Can also swap any objects • As long as = op is public CS3101-2, Lecture 5
Fancier swapping • Remember our fancier swap ftn? • void swap(int &a, int &b) { a ^= b ^= a ^= b; } • Fancier template function: • template <class T>void swap(T &a, T &b) { a ^= b ^= a ^= b; } • Now can swap ints, chars, longs • But: cannot swap objects • Unless their ^= is overloaded – unlikely CS3101-2, Lecture 5
Template specialization • string s,t … max(s,t); works • But max(“hi”,”there”) doesn’t: • if (“hi” < “there”) … compares two pointers - where the char[]s start • Not what we mean • Soln: create a specialization • special version for this case • We check for spec. before template • char *max(char *a, char *b) { return strcmp(a,b) > 0 ? a : b; } CS3101-2, Lecture 5
Class templates • Couple weeks ago: wrote a stack class • supported only integers • We’ll abstract element type away • “Abstract data types” • Only changes to declar: • prepend on class dfn: • template <class T>class className {…} 2. Replace int T • For ftn implems, we • prepend the same • replace className with className<T> • Replace int T • template <class T> void Stack<T>::push(const T elm){} • To instantiate: • Stack<string> strStack; CS3101-2, Lecture 5
Class specialization • Similarly, can specialize member functions of class templates: • void stack<char*>::push(const char *const item) { data[count++] = item; } CS3101-2, Lecture 5
Templates & statics • Review: static data members • one inst shared by all class insts • What about statics in templates classes? • Q: Could one inst be shared by all insts? • A: No – consider: • template <class T> class C { static T mem; … } • mem couldn’t me shared by all insts • shared by all insts • But: for C<int>, mem shared by all C<int> insts CS3101-2, Lecture 5
Templates & friends • Given class, can declare some outside ftn or class its friend • We have a stack class • Suppose: want to declare external sort ftn its friend • Before: had stack with ints • could use sort ftn based on ints • Now: have Stack<T> • friend is template too • template <class T> class Stack { friend void C<T>::f5(X<T>); … CS3101-2, Lecture 5
Odds and ends: Forward declarations • Suppose classes Cat and Dog each depend on each other • class Cat { void look(Dog d) { cout << “Meow!\n”; } }; • class Dog { void look(Cat c) { cout << “Bark!\n”; } }; • Q: Will this compile? • A: No - Dog is referenced before declared • Soln: a forward declaration • Put class Dog; beforeCat def • Dog not yet complete but • Dog will now be recognized, w/o Cat depend. CS3101-2, Lecture 5
Namespaces • int i; • namespace Example { double PI = 3.14; int i = 8; void printVals(); namespace Inner { int i = 9; } } // no semi-colon! • Can access: • Example::i, Example::Inner::i • printVals implementation: • void Example::printVals() { i is Example::i ::i is the global I } CS3101-2, Lecture 5
Namespaces • Now: can use • Example • Example::Inner • Example::Inner::i • Example::i • Nested namespaces ~ Java packages • Unfortly: #include (CPP) / using (C++) independent • In general, use maximally narrow ranges • Prevent ambiguity • Don’t say using namespace std; • Or can fully specify reference: • std::std << std::endl; CS3101-2, Lecture 5
assert • Old days: bad thing happens • writing to bad memory address • divide by 0, etc. • core dump, maybe don’t notice, etc. • void Stack::push(const int item) { data[count++] = item; } • no room overwrite wrong data, crash, etc. • Somewhat better: assert that everything is okay • assert(count >= 0 && count < sizeof(data)/sizeof(data[0])); • “Everything’s okay, right?” • If false, we quit with message of false expression CS3101-2, Lecture 5
Exceptions • Now: to some extent • bad behavior is prevented • attempt “exception” • If bad things happen • we halt, tell calling ftn • maybe it halts, tells its calling ftn • eventually, either • someone responds accordingly or • main ftn passes to OS • try – throw – catch • try to do something • maybe an exception gets thrown • if so, we may catch it, and go on CS3101-2, Lecture 5
Exception handling • void Stack::push(const int item) throws BoundExp { if (count < 0 || count >= sizeof(data)/sizeof(data[0])) throw BoundExp(“stack overflow”); data[count++] = data; //ok if here } • What is BoundExp? • A class we define CS3101-2, Lecture 5
Our exception • class BoundExp: exception { public: BoundExp(const string &s) exception(s) {} }; • NB: It’s just a class • Its parent is exception but needn’t be • Exception has what() • maybe other info CS3101-2, Lecture 5
Throwing and catching • try { Stack s; … s.push(25); … } catch (BoundExp &exp) { cout << “Error: “ << exp.what() << ‘\n’; } catch (ExpType2 &exp) { // can catch mult kinds // only catch <= 1 … } catch (…) { // … is a wildcard! cout << “Unknown exception caught.\n”; } CS3101-2, Lecture 5
Exception classes • <exception> exception • <stdexcept> • runtime_error, logic_error • bad_alloc: new failed • bad_cast: dynamic_cast failed • Can throw non-exception objs • And even primitives • But handling easer if don’t CS3101-2, Lecture 5
STL • Ceteris paribus, libraries are good • Hard, subtle problems many mistakes • don’t re-invent the wheel • Unless we’re wheel artists • better to commodify the wheel • Use an “off-the-shelf” wheel like everyone else • The standard wheel is reliable and efficient • STL == Starbucks of programming • Lots of important algorithms, data structures in CS • Barring good reason • use std versions CS3101-2, Lecture 5
Standard Template Library • Many template classes, functions • Abstract data types • Three general categories: • Containers • Iterators • Algorithms • Three kinds of containers: • Sequences • Associative • Adapted CS3101-2, Lecture 5
STL: “first-class” containers • Sequences: • vector: Dynamic-array-backed • const-time random-access • const-time insert/delete at back • deque: double-ended queue • fast random-access - how? • fast insert/delete at front and back • list: doubly-linked list • fast insert/delete anywhere • Associative: • set: non-sequential, unique • multiset: non-sequential, non-unique • map: maps from keys to unique values • multimap: maps to non-unique values CS3101-2, Lecture 5
STL: containers • Container adapters: • use “first-class” containers by composition • stack: LIFO • queue: FIFO • priority_queue • Near-containers: • arrays • string • bitset • valarray CS3101-2, Lecture 5
Container member ops & ftns • copy constructor • empty() • size() • swap • First-class: • begin() • end() • rbegin() • rend() • erase • clear() NB: These are allow very simple - little more than getters CS3101-2, Lecture 5
STL Iterators • Standard way to traverse through container: iteration • Abstraction of both “index” and “pointer” • just: means of iterating • forward, back, etc. • Iterator direction types: • Forward iterator • Reverse iterator • both supported by vector, list, etc. • Random-access iterator • supported by vector • Also: its can be const or not CS3101-2, Lecture 5
Types of iterators • I/O iterators are “one-pass” • can only move in one direction • can only traverse once – p++ • Other types: • bidirectional: p++, p-- • random-access: p + i, p - i, p[i] *(p+i), p1 < p2 • vector: random-access • deque: random-access • list: bidirectional • set/multiset: bidirectional • map/multimap: bidirectional CS3101-2, Lecture 5
vector class • Most commonly used container class • Fast random access • random-access iterators • Can access mems • with []s like arrays – unsafe • with at(i) – checks bounds, throws exception – safer • Essentially: dynamic array hidden in obj • add to/delete from back: const time • unless run out of space • autoly copy to larger array • insert/del from middle: linear time • must move half of mems forward/back CS3101-2, Lecture 5
Vectors <vector> • Similar to Java’s Vector in that: • dynamic-array-backed list • same complexities • Different in that: • takes insts of specified type • vector<int> nums; • vector<double> vals(20); • size-20 vector of doubles • vector<Base> objs; • takes Base objects • vector<Base*> ptrs; • takes Base*s or Extended*s CS3101-2, Lecture 5
Template errors can be illegible • Consider this ftn: • template <class T> void printReverse(const vector<T> &vect) { for (vector<T>::reverse_iterator curr = vect.rbegin(); curr != vect.rend(); curr++) cout << *curr << ","; } • Slightly different from before • how? CS3101-2, Lecture 5
Template errors can be illegible • When compiled: • Error E2034 c:\Borland\Bcc55\include\rw/iterator.h 442: Cannot convert 'const int *' to 'int *' in function reverse_iterator<int *>::reverse_iterator(const reverse_iterator<const int *> &) • Error E2094 vect.cpp 19: 'operator!=' not implemented intype 'reverse_iterator<int *>' for arguments of type 'reverse_iterator<const int *>' in function printReverse<int>(const vector<int,allocator<int> > &) • Error E2034 c:\Borland\Bcc55\include\rw/iterator.h 442: Cannot convert 'const int *' to 'int *' in function reverse_iterator<int *>::reverse_iterator(const reverse_iterator<const int *> &) • Warning W8057 c:\Borland\Bcc55\include\rw/iterator.h 442: Parameter 'x' is never used in function reverse_iterator<int *>::reverse_iterator(const reverse_iterator<const int *> &) • *** 3 errors in Compile *** • Why? reverse_iterator not const_reverse_iterator CS3101-2, Lecture 5
Vectors e.g. – vect.cpp • template <class T> ostream& op<<(ostream& out, const vector<T> &vect) { out << "("; for (int i = 0; i < vect.size(); i++) out << vect[i] << ","; out << ")"; return out; } • template <class T> void printReverse(const vector<T> &vect) { cout << "("; for (vector<T>::const_reverse_iterator curr = vect.rbegin(); curr != vect.rend(); curr++) cout << *curr << ","; cout << ")"; } CS3101-2, Lecture 5
Vectors e.g. – vect.cpp • void main() { srand(time(NULL)); vector<int> ints; cout << "Initial size == " << ints.size() << "\nInitial capacity == " << ints.capacity(); for (int i = 0; i < 5; i++) ints.push_back(rand() % 20); cout << "\nNow, size == " << ints.size() << "\nCapacity == " << ints.capacity(); cout << "\nvector: " << ints; cout << "\nvector reversed: "; printReverse(ints); CS3101-2, Lecture 5
Vectors e.g. – vect.cpp • try { ints.at(100) = 20; } catch (out_of_range oor) { cout << "\nTried to set mem 100,"; cout << "\nbut caught exception: " << oor.what(); } sort(ints.begin(), ints.end()); cout << "\nAfter sort, vect: “ << ints; • } CS3101-2, Lecture 5
Vectors e.g. – vect.cpp • Initial size == 0 Initial capacity == 0 Now, size == 5 Capacity == 256 vector: (7,3,16,14,17,) vector reversed: (17,14,16,3,7,) • Tried to set mem 100 to 20, • but caught exception: index out of range in function: vector:: at(size_t) index: 100 is greater than max_index: 5 • After sort, vector: (3,7,14,16,17,) CS3101-2, Lecture 5
STL interators – iterio.cpp • Access set of values from one place • Usually, place is a container • But: input stream may be construed as a place • #include <iostream> #include <iterator> • using namespace std; • void main() { cout << “Enter two nums: “; istream_iterator<int> intIn(cin); int x = *intIn; intIn++; x += *intIn; ostream_iterator<int> intOut(cout); cout << “The sum is: “; *intOut = x; cout << endl; } CS3101-2, Lecture 5
I/O iterators – ioiter.cpp • Code: • int x = *intIn; intIn++; x += *intIn; • Output: • C:\3101-2\lec5>ioiter Enter two nums: 5 6 The sum is: 11 • But if code: • int x = *intIn; /*intIn++;*/ x += *intIn; • Then output: • C:\3101-2\lec5>ioiter Enter two nums: 5 6 The sum is: 10 CS3101-2, Lecture 5
copy function – vect2.cpp • Another way to print container: • use copy function • if (!vect.empty()) { ostream_iterator<T> out(cout, " "); copy(vect.begin(), vect.end(), out); } • copy(src begin it, src end it, dest it); • src begin it: vect.begin() • src end it: vect.end() • dest it: ostream_iterator<T> out(cout, " ") • it’s an ostream_iterator • it’s wrapping around cout • it’s outputting Ts • it’s printing “ “ between the Ts CS3101-2, Lecture 5