CS253: Software Development with C++

Spring 2019

Exception Handling

Show Lecture.ExceptionHandling as a slide show.

CS253 Exception Handling

Basic Syntax

try { … }
Execute some code, which might throw an exception.
throw value;
Throw an exception. At that point, program execution transfers to any applicable catch clause. If no catch clause applies, then the program dies.
catch(specification) { … }
Deal with an exception.
specification is very much like a function argument declaration.

Normal Operation

void snap() {
    cout << "mind\n";
    throw "Thanos";
    cout << "power\n";  // 💀💀💀
}

int main() {
    cout << "reality\n";
    try {
        cout << "soul\n";
        snap();
        cout << "space\n";  // 💀💀💀
    }
    catch (const char *villain) {
        cout << "Caught " << villain << '\n';
    }
    cout << "time\n";
}
reality
soul
mind
Caught Thanos
time

throw without catch

If something is thrown but never caught, then the special function terminate() is called, which complains in an implementation-defined manner, and stops the program by calling abort().

throw "ouch";
terminate called after throwing an instance of 'char const*'
SIGABRT: Aborted

Objects get destroyed appropriately

#include "Loud.h"

Loud a('a');

void foo() {
    Loud b('b');
    Loud c('c');
}

int main() {
    Loud d('d');
    foo();
    Loud e('e');
    return 0;
}
Loud::Loud() [c='a']
Loud::Loud() [c='d']
Loud::Loud() [c='b']
Loud::Loud() [c='c']
Loud::~Loud() [c='c']
Loud::~Loud() [c='b']
Loud::Loud() [c='e']
Loud::~Loud() [c='e']
Loud::~Loud() [c='d']
Loud::~Loud() [c='a']

throw without catch

#include "Loud.h"

Loud a('a');

void foo() {
    Loud b('b');
    throw 42;
    Loud c('c');
}

int main() {
    Loud d('d');
    foo();
    Loud e('e');
    return 0;
}
Loud::Loud() [c='a']
Loud::Loud() [c='d']
Loud::Loud() [c='b']
terminate called after throwing an instance of 'int'
SIGABRT: Aborted

You can throw anything—doesn’t have to be a special type or object.

I mean anything

Really—you can throw and catch any type. For example:

throw 42;
terminate called after throwing an instance of 'int'
SIGABRT: Aborted
throw "alpha";
terminate called after throwing an instance of 'char const*'
SIGABRT: Aborted
throw "beta"s;
terminate called after throwing an instance of 'std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >'
SIGABRT: Aborted

I mean anything

int n=42; throw "Value no good: " + to_string(n);
terminate called after throwing an instance of 'std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >'
SIGABRT: Aborted
throw logic_error("That failed miserably");
terminate called after throwing an instance of 'std::logic_error'
  what():  That failed miserably
SIGABRT: Aborted
range_error r("value too large"); throw r;
terminate called after throwing an instance of 'std::range_error'
  what():  value too large
SIGABRT: Aborted

try #1

#include "Loud.h"

Loud a('a');

void foo() {
    Loud b('b');
    throw "oops!";
    Loud c('c');
}

int main() {
    Loud d('d');
    try {
        foo();
    }
    catch (const char *error) {
        cout << "Caught: " << error << "\n";
    }

    Loud e('e');
    return 0;
}
Loud::Loud() [c='a']
Loud::Loud() [c='d']
Loud::Loud() [c='b']
Loud::~Loud() [c='b']
Caught: oops!
Loud::Loud() [c='e']
Loud::~Loud() [c='e']
Loud::~Loud() [c='d']
Loud::~Loud() [c='a']

try #2

#include "Loud.h"

Loud a('a');

void foo() {
    Loud b('b');
    throw "oops!";
    Loud c('c');
}

void bar() {
    Loud d('d');
    foo();
}

int main() {
    Loud e('e');
    try {
        bar();
    }
    catch (const char *error) {
        cout << "Caught: “" << error << "”\n";
    }

    Loud f('f');
    return 0;
}
Loud::Loud() [c='a']
Loud::Loud() [c='e']
Loud::Loud() [c='d']
Loud::Loud() [c='b']
Loud::~Loud() [c='b']
Loud::~Loud() [c='d']
Caught: “oops!”
Loud::Loud() [c='f']
Loud::~Loud() [c='f']
Loud::~Loud() [c='e']
Loud::~Loud() [c='a']

catch #1

try {
    throw "oops!";
}

catch (int i) {
    cout << "int " << i << "\n";
}

catch (const char *error) {
    cout << "C string: " << error << "\n";
}

catch (...) {  // Gotta catch ’em all!
    cout << "something\n";
}
C string: oops!

Multiple catches

try { }
catch (...) { }
catch (int) { }
c.cc:2: error: '...' handler must be the last handler for its try block

catch #2

try {
    throw 42;
}

catch (short s) {
    cout << "Got a short: " << s << "\n";
}

catch (long l) {
    cout << "Got a long: " << l << "\n";
}
terminate called after throwing an instance of 'int'
SIGABRT: Aborted

The type must match. No conversions!

catch #2½

try {
    throw "Son of a gun!";
}

catch (string s) {
    cout << "Got a string: " << s << "\n";
}
terminate called after throwing an instance of 'char const*'
SIGABRT: Aborted

Why didn’t that work?

catch #2¾

try {
    throw "That’s better."s;
}

catch (string s) {
    cout << "Got a string: " << s << "\n";
}
Got a string: That’s better.

Hey, that worked!

catch #2⅞

try {
    throw "Even better."s;
}

catch (const string &s) {
    cout << "Got a string: " << s << "\n";
}
Got a string: Even better.

This works, and is more efficient. Of course, efficiency may not be an issue here.

catch #3

// logic_error is-a exception
// runtime_error is-a exception
// overflow_error is-a runtime_error

try {
    throw overflow_error("Just too big!");
}
catch (const logic_error &e) {
    cout << "logic_error: " << e.what() << '\n';
}
catch (const runtime_error &e) {
    cout << "runtime_error: " << e.what() << '\n';
}
catch (const exception &e) {
    cout << "exception: " << e.what() << '\n';
}
runtime_error: Just too big!

catch #4

// logic_error is-a exception
// runtime_error is-a exception
// overflow_error is-a runtime_error

try {
    throw overflow_error("Just too big!");
}
catch (const logic_error &e) {
    cout << "logic_error: " << e.what() << '\n';
}
catch (const exception &e) {
    cout << "exception: " << e.what() << '\n';
}
exception: Just too big!

No conversions, but is-a is good enough.

re-throw

void foo() {
    throw string("Division by zero");
}

void bar() {
    try {
        foo();
    }
    catch (string msg) {
        if (msg == "Out of memory")  // I’ve got this!
            /* get more memory */;
        else
            throw;      // Throw up hands in despair.
    }
}

int main() {
    try {
        bar();
    }
    catch (string problem) {
        cout << "Problem in bar: " << problem << '\n';
    }

    return 0;
}
Problem in bar: Division by zero

User: Guest

Check: HTML CSS
Edit History Source

Modified: 2019-05-13T14:50

Apply to CSU | Contact CSU | Disclaimer | Equal Opportunity
Colorado State University, Fort Collins, CO 80523 USA
© 2018 Colorado State University
CS Building