CS102 C++ Exception Handling & Namespaces Bill Cheng http://merlot.usc.edu/cs102-s12 1
Topics to cover C Structs (Ch 10) C++ Classes (Ch 11) Constructors Destructors Member functions Exception Handling (Ch 15) Namespaces (Ch 8) Operator Overloading (Ch 14) Class Composition & Inheritance (Ch 12) Pointers & Dynamic Objects (Ch 13) Polymorphism (Ch 13) Virtual functions Abstract classes Interfaces C++ Object-Oriented Programming 2
Exception Handling When something goes wrong in one of your functions, how should you notify to the function caller? Return a special value from the function? Return a bool indicating success/failure? Set a global variable? Print out an error message? Print an error and exit the program? Set a failure flag on somewhere (like "cin" does)? Handle the problem and just don t tell the caller? 3
Exception Handling There s something wrong with all those options... You should always notify the caller something happened. Silence is not an option. You can t always return an error state (what if a function is supposed to return "bool"?) What if something goes wrong in a Constructor? You don t have a return value available What if the function where the error happens isn t equipped to handle the error? All these strategies are passive. They require the caller to actively check if something went wrong. 4
The "assert" Statement The assert statement allows you to make sure certain conditions are true and immediately halt your program if they re not Good sanity checks for development/testing Not ideal for an end product #include <cassert> int divide(int num, int denom) assert(denom!= 0); return(num/denom); 5
The "throw" Statement Used when code has encountered a problem, but the current code can t handle that problem itself #include <cassert> int divide(int num, int denom) if(denom == 0) throw denom; return(num/denom); throw interrupts the normal flow of execution If nothing deals with it, the program will terminate Gives the caller the opportunity to catch and handle it What can you give to the throw statement? Anything! But some things are better than others... 6
What Should You "throw"? Don t throw primitive values (e.g. an "int") throw 123; The value that is thrown may not always be meaningful Provides no other context (what happened & where?) Don t throw "string" throw "Someone passed in a 0 and stuff broke!"; Works for a human, but not much help to an application Use the <stdexcept> header file throw std::invalid_argument( "Denominator can t be 0!"); Serves as the basis for building your own exceptions Have a method called "what()" with extra details You can always make your own exception class too! 7
The "try" and "catch" Statements try & catch are the companions to throw A try block surrounds any code that may throw A catch block lets you handle exceptions if a throw does happen (you can have multiple catch blocks) try divide(numerator,denominator); catch(int& badvalue) cerr << "Can t use value " << badvalue << endl; //do other stuff 8
The "try/catch" Flow try cout << "This code is fine." << endl; throw 0; //some code that always throws cout << "This will never print." << endl; catch(int& x) cerr << "The throw immediately comes here." << endl; catch(string& y) cerr << "We won t hit this catch." << endl; cout << "Everything goes back to normal here." << endl; This example just illustrates functionality...it s not that useful 9
cin Error Handling (the Old Way) #include <iostream> using namespace std; int main() int number = 0; cout << "Enter a number: "; cin >> number; if(cin.fail()) cerr << "That was not a number." << endl; cin.clear(); cin.ignore(1000, \n ); 10
cin Error Handling (the New Way) #include <iostream> using namespace std; int main() //tell "cin" it should throw cin.exceptions(iostream::failbit); //or cin.exceptions(istream::failbit); //or cin.exceptions(ios::failbit); //or cin.exceptions(ios_base::failbit); int number = 0; try cout << "Enter a number: "; cin >> number; catch(iostream::failure &ex) cerr << "That was not a number." << endl; cin.clear(); cin.ignore(1000, \n ); cout << "number = " << number << endl; 11
Vector Indexing (the Old Way) #include <iostream> #include <vector> using namespace std; int main() int index = -1; vector<int> list(5,0); if(index < 0 index >= list.size()) cerr << "Your index was out of range!" << endl; else cout << "Value is: " << list[index] << endl; 12
Vector Indexing (the New Way) #include <iostream> #include <vector> #include <stdexcept> using namespace std; int main() int index = -1; vector<int> list(5,0); try cout << "Value is: " << list[index] << endl; catch(out_of_range &ex) cerr << "Your index was out of range!" << endl; 13
Advantages/Disadvantages The Good Give the function caller a choice on how (or if) they want to handle an error Don t assume you know what the caller wants Decouple the exception processing logic from the normal control flow of the code They make for much cleaner code (usually) Exceptions can propagate up the call hierarchy ("Unwinding the call stack") Exceptions are sort of "duct taped" onto C++ The Bad Hard to tell if/when a function will throw Being able to throw things like "int" isn t very useful 14
Other throw/try/catch Notes Do not use throw from a destructor. Your code will go into an inconsistent (and unpleasant) state. Or just crash. You can use the catch(...) (a.k.a. catch everything) statement to catch any exception, but it s usually not that desirable try //code that throws something catch(...) cerr << "I got an error, but I have no context!" << endl; 15
Other "throw/try/catch" Notes You can re-throw an exception you ve caught Useful if you want to take intermediate action, but can t actually handle the exception try //code that throws something catch(someexception& se) cerr << "I got an error!" << endl; throw; //throws "se" again from here 16
Notes On Exams Now that you know about exceptions In exams, when you detect an error condition, you should know that you can just throw an exception Please don t say, "I assume that an error condition cannot happen" Please don t ask, "What should I do about error conditions?" <stdexcept> Logic errors logic_error domain_error invalid_argument length_error out_of_range Runtime errors runtime_error range_error overflow_error underflow_error 17
CS102 C++ Namespaces Bill Cheng http://merlot.usc.edu/cs102-s12 18
Namespaces A high-level grouping mechanism for class, function and variable definitions NOTE: Only applies to things declared at the global level (e.g. not variables within functions) Why do we need them? Avoid naming conflicts Especially when using #include with other people s stuff Group related functionality Example: Spreadsheet for a furniture store Table of Tables 19
Defining Namespaces How do we declare a namespace? namespace SOME_NAME //code goes here By default, everything is in the global namespace If you declare the same namespace block multiple times (e.g. "namespace XYZ... ") then everything will be merged into one big namespace You already know that most everything in <iostream> is declared in the namespace "std" 20
Putting Namespaces to Use Namespaces are used with the "scope resolution operator" (a.k.a. the "::") The default global namespace is just "::" It s assumed if you don t specify it Prefix your item with "name::" to use a namespace std::cout << "Blah blah blah" << std::endl; This should look really similar to defining functions for a class Classes are an implicit namespacing/scoping mechanism! 21
The "using" Statement Normally you always have to use an item s namespace std::cout << "Hello world!" << std::endl; The using keyword lets you pull thing out of other namespaces into your current namespace You can pull individual items in: using std::cout; cout << "Hello world!" << std::endl; You can pull the entire namespace in (be careful!): using namespace std; cout << "Hello world!" << endl; Don t specify using in your header (.h) files! (bad style don t assume you know what others want) 22
CS102 Extra Slides Bill Cheng http://merlot.usc.edu/cs102-s12 23
assert.cpp /* * 1) Create a function divide(). It asserts that denominator * cannot be 0. * 2) In main() read in two integers and call divide() and * print the return value. * 3) If would not compile, probably because forgot to * #include <cassert>. */ 24
#include <iostream> #include <cassert> using namespace std; int divide(int num,int denom) assert(denom!= 0); return(num/denom); int main() int n,d; cout << "Enter: "; cin >> n >> d; assert.cpp cout << "Result = " << divide(n,d) << endl; 25
throw.cpp /* * 1) Demonstrate how to use throw, try & catch. * 2) Show how to make your own exception class. * 3) Create a class DivideByZeroError to throw. (Cannot * inherit this from one of the existing exception * class because we haven t covered exception yet.) * 4) Write the divide() function. If denominator is 0, * throw an instance of class above. If any of the * input is negative, throw the out_of_range system * exception. * 5) Write a function foo() that calls divide() using try * and catch. Make it so that it will only catch the * out_of_range exception. * 6) In main(), read two integers, calls foo() in the * try block and have 3 catch blocks. One to catch * out_of_range, one to catch DivideByZeroError, and * one to catch everything else. * 7) Print the return value of foo(). * 8) Try entering values like "10 2", "10 0" and "10-2" from * the console to see the different behaviors. * 9) If would not compile, probably because forgot to * #include <stdexcept>. */ 26
#include <iostream> #include <string> #include <stdexcept> #include <vector> using namespace std; class DivideByZeroError public: DivideByZeroError() ; throw.cpp int divide(int num,int denom) if(denom == 0) DivideByZeroError err; throw err; //alternate syntax: //throw DivideByZeroError; else if(num < 0 denom < 0) throw out_of_range("negative values."); return(num/denom); int foo(int num,int denom) try return divide(num,denom); catch(out_of_range& oor) cerr << "In foo() = " << oor.what() << endl; 27
int main() int n,d; cout << "Enter: "; cin >> n >> d; throw.cpp (Cont...) int result; try result = foo(n,d); cout << "Still in the try block after foo..." << endl; catch(out_of_range& oof) cerr << "in main() = " << oof.what() << endl; catch(dividebyzeroerror& ex) cerr << "We divided by zero!" << endl; catch(...) //this statement is never triggered in this code, but it s //the syntax for a "catch all" that will catch anything that //gets thrown from within the "try" block cout << "Result = " << result << endl; 28
namespaces.cpp /* * 1) Demonstrate how to use namespace. * 2) Don t do "using namespace std"; * 3) Note that most things in "iostream" and "string" are * in the namespace "std" by default. * 4) Define a global int variable y. This is in the * default namespace. * 5) Create a namespace called cs102. Define a class Dummy * in it with a method called bar() that returns a * std::string. * 6) Using a separate namespace block to put more stuff * into the cs102 namespace. Define a function foo() * to use std::cout and std::endl. * 7) In main(), do "using std::endl;" so that we can just * say "endl" without pulling the whole std namespace * into main(). * 8) Define an int named cout and print it. * 9) Call foo() from the cs102 namespace. * 10) Create an instance of Dummy and call its bar() member * function. * 12) Create a local int variable y. Print the local * variable y. Print the global variable y in the * default namespace. */ 29
#include <iostream> #include <string> int y = 0; namespace cs102 class Dummy std::string bar() ; return "this is bar()"; namespace cs102 void foo() namespaces.cpp int main() using std::endl; int cout = 5; std::cout << cout << endl; cs102::foo(); cs102::dummy d; std::cout << d.bar() << endl; int y = 99; std::cout << y << endl; std::cout << ::y << endl; std::cout << "foo() is in the " << "cs102 namespace" << std::endl; 30