COMP2004
Programming Practice
2002 Summer School
Kevin Pulo
School of Information Technologies
University of Sydney
(page 1)
Associative Containers
- Associate values with keys
- Provide efficient insertion/retrieval
- Maintain an internal ordering
- May differ with library implementations
- ie. you can't trust the internal order
(page 2)
Maps
- Maps are the most common associative container
- Correspond to hashtables/hashmaps
- Operator overloading makes them easy to use
(page 3)
Simple Map Example
#include
(page 4)
Accessing map items
- map[key]
- Each key has only one value
- Returns reference to value for key
- Can modify the value
- Creates key and value if needed
(page 5)
Map Iterator Example
#include
(page 6)
Pair
- Dereferencing a map iterator gives a pair object
- A pair is a simple data structure
- The first member gives the key
- The second member gives the value
- You can use pairs directly in your code
- ie. first doesn't have to be a key, second doesn't have to be a value
- You can use anywhere you need to store two things together
(page 7)
Manually inserting into a map
map m;
- then can insert a pair with
pair p("test", 42);
m.insert(p);
m.insert(pair("test", 42));
(page 8)
Set
- Similar to a map
- Has only a key, with no value
- Useful when you only care about existence
(page 9)
set example
#include
using namespace std;
int main() {
set nums;
int val;
while (cin >> val)
nums.insert(val);
for (set::const_iterator i =
nums.begin(); i != nums.end(); ++i)
cout << *i << endl;
}
(page 10)
Testing Existance
- How do you check if a particular key exists?
- Iterating over the set/map too be slow
- Both set and map provide a find() method
- It returns an iterator
- To the entry with that key, if found
- end() if key not found
(page 11)
set find() example
set s;
set::const_iterator si = s.find("it");
if (si != s.end())
cout << "Found it" << endl;
else
cout << "Didn't find it" << endl;
(page 12)
Another set find()
int main() {
set nums;
int val;
while (cin >> val)
nums.insert(val);
if (nums.find(42) != nums.end())
cout << "42 entered sometime";
else
cout << "42 not entered";
}
(page 13)
map find() example
map m;
map::const_iterator mi
= m.find("it");
if (mi != m.end())
cout << "It is : " << mi->second
<< endl;
else
cout << "Didn't find it" << endl;
(page 14)
Another map find()
int main() {
map count;
string s;
while (cin >> s) count[s]++;
if (count.find("hello") != count.end())
cout << "You said hello";
else
cout << "You never said hello";
}
(page 15)
Removing from a map/set
- Use the erase() method
- Can take a key:
m.erase(key);
m.erase(m.find(key));
- Some STL implementations have a buggy key version
- If so, just use the iterator version
(page 16)
multimap / multiset
- There are also 'multi' versions of map and set
- They allow multiple identical keys
- multimaps are not used too often
(page 17)
multiset Example
#include
int main() {
multiset nums;
int val;
while (cin >> val)
nums.insert(val);
for (multiset::const_iterator i =
nums.begin(); i != nums.end(); ++i)
cout << *i << endl;
}
(page 18)
Exceptions
- Allows seperation of error handling
- Detect errors locally
- Handle errors where appropriate
- Has a performance impact with most compilers
(page 19)
C++ Exceptions
- Exceptions can be of any type
- Could throw a vector, int, string, Person, or whatever
- Usually it is best to use specific exception classes
(page 20)
Throwing An Exception
struct Domain_Error {
int number;
Domain_Error(int d) : number(d) { }
};
double square_root(int n) {
if (n < 0) {
throw Domain_Error(n);
}
return sqrt(n);
}
(page 21)
Catching An Exception
int main() {
int num;
while (cin >> num) {
try {
cout << square_root(num) << '\n';
} catch(Domain_Error e) {
cerr << e.number
<< " is not valid\n";
}
}
}
(page 22)
Uncaught exceptions
- If an exception is thrown in a function but not caught, it goes to the calling function
- This continues all the way to main()
- If it's still not caught by main(), the program exits
- Usually with the message
Aborted (core dumped)
- Can then use gdb as normal
(page 23)
Multiple Catchers
try {
// do something...
} catch (SomeType st) {
// handle SomeType exception
} catch (SomeOtherType sot) {
// handle SomeOtherType exception
} catch (...) {
// handle anything else
}
(page 24)
Multiple Catchers II
- First catch type that matches is used
- So with inheritance
- Derived classes must come first
- Otherwise the base will match
- Inherited exceptions can be useful
class IOError { };
class NoFileError : public IOError { };
try { } catch (IOError e) { }
(page 25)
Rethrowing Exceptions
- A catch block can throw an exception
- All remaining catch blocks on the same level are ignored
- The original exception can be rethrown
- throw;
- Even in a catch(...) block
(page 26)
try {
try {
throw 10;
} catch (int i) {
cerr << "Caught : " << i << endl;
throw 20.0;
} catch (...) {
cerr << "Won't reach" << endl;;
}
} catch (int i) {
cerr << "int : " << i << endl;
} catch (double d) {
cerr << "double : " << d << endl;
throw;
}
(page 27)
Exception Specifications
- Functions can declare what exceptions they throw
- void func() throw (e1, e2);
- func can throw types e1 and e2
- Or classes inheriting from them
- void func() throw ();
- func can not throw any exceptions
- void func();
- func can throw any exceptions
(page 28)
Function Pointers
- The exception specifications of a function pointer must match the function
void foo() throw (X);
void foo2();
void (*pf1)() throw (X,Y) = &foo; // OK
void (*pf2)() throw () = &foo; // error
void (*pf3)() throw (X) = &foo2; // error
(page 29)