
- •New to the Tenth Edition
- •Preface
- •Acknowledgments
- •About the Author
- •Contents
- •1.1 Reasons for Studying Concepts of Programming Languages
- •1.2 Programming Domains
- •1.3 Language Evaluation Criteria
- •1.4 Influences on Language Design
- •1.5 Language Categories
- •1.6 Language Design Trade-Offs
- •1.7 Implementation Methods
- •1.8 Programming Environments
- •Summary
- •Problem Set
- •2.1 Zuse’s Plankalkül
- •2.2 Pseudocodes
- •2.3 The IBM 704 and Fortran
- •2.4 Functional Programming: LISP
- •2.5 The First Step Toward Sophistication: ALGOL 60
- •2.6 Computerizing Business Records: COBOL
- •2.7 The Beginnings of Timesharing: BASIC
- •2.8 Everything for Everybody: PL/I
- •2.9 Two Early Dynamic Languages: APL and SNOBOL
- •2.10 The Beginnings of Data Abstraction: SIMULA 67
- •2.11 Orthogonal Design: ALGOL 68
- •2.12 Some Early Descendants of the ALGOLs
- •2.13 Programming Based on Logic: Prolog
- •2.14 History’s Largest Design Effort: Ada
- •2.15 Object-Oriented Programming: Smalltalk
- •2.16 Combining Imperative and Object-Oriented Features: C++
- •2.17 An Imperative-Based Object-Oriented Language: Java
- •2.18 Scripting Languages
- •2.19 The Flagship .NET Language: C#
- •2.20 Markup/Programming Hybrid Languages
- •Review Questions
- •Problem Set
- •Programming Exercises
- •3.1 Introduction
- •3.2 The General Problem of Describing Syntax
- •3.3 Formal Methods of Describing Syntax
- •3.4 Attribute Grammars
- •3.5 Describing the Meanings of Programs: Dynamic Semantics
- •Bibliographic Notes
- •Problem Set
- •4.1 Introduction
- •4.2 Lexical Analysis
- •4.3 The Parsing Problem
- •4.4 Recursive-Descent Parsing
- •4.5 Bottom-Up Parsing
- •Summary
- •Review Questions
- •Programming Exercises
- •5.1 Introduction
- •5.2 Names
- •5.3 Variables
- •5.4 The Concept of Binding
- •5.5 Scope
- •5.6 Scope and Lifetime
- •5.7 Referencing Environments
- •5.8 Named Constants
- •Review Questions
- •6.1 Introduction
- •6.2 Primitive Data Types
- •6.3 Character String Types
- •6.4 User-Defined Ordinal Types
- •6.5 Array Types
- •6.6 Associative Arrays
- •6.7 Record Types
- •6.8 Tuple Types
- •6.9 List Types
- •6.10 Union Types
- •6.11 Pointer and Reference Types
- •6.12 Type Checking
- •6.13 Strong Typing
- •6.14 Type Equivalence
- •6.15 Theory and Data Types
- •Bibliographic Notes
- •Programming Exercises
- •7.1 Introduction
- •7.2 Arithmetic Expressions
- •7.3 Overloaded Operators
- •7.4 Type Conversions
- •7.5 Relational and Boolean Expressions
- •7.6 Short-Circuit Evaluation
- •7.7 Assignment Statements
- •7.8 Mixed-Mode Assignment
- •Summary
- •Problem Set
- •Programming Exercises
- •8.1 Introduction
- •8.2 Selection Statements
- •8.3 Iterative Statements
- •8.4 Unconditional Branching
- •8.5 Guarded Commands
- •8.6 Conclusions
- •Programming Exercises
- •9.1 Introduction
- •9.2 Fundamentals of Subprograms
- •9.3 Design Issues for Subprograms
- •9.4 Local Referencing Environments
- •9.5 Parameter-Passing Methods
- •9.6 Parameters That Are Subprograms
- •9.7 Calling Subprograms Indirectly
- •9.8 Overloaded Subprograms
- •9.9 Generic Subprograms
- •9.10 Design Issues for Functions
- •9.11 User-Defined Overloaded Operators
- •9.12 Closures
- •9.13 Coroutines
- •Summary
- •Programming Exercises
- •10.1 The General Semantics of Calls and Returns
- •10.2 Implementing “Simple” Subprograms
- •10.3 Implementing Subprograms with Stack-Dynamic Local Variables
- •10.4 Nested Subprograms
- •10.5 Blocks
- •10.6 Implementing Dynamic Scoping
- •Problem Set
- •Programming Exercises
- •11.1 The Concept of Abstraction
- •11.2 Introduction to Data Abstraction
- •11.3 Design Issues for Abstract Data Types
- •11.4 Language Examples
- •11.5 Parameterized Abstract Data Types
- •11.6 Encapsulation Constructs
- •11.7 Naming Encapsulations
- •Summary
- •Review Questions
- •Programming Exercises
- •12.1 Introduction
- •12.2 Object-Oriented Programming
- •12.3 Design Issues for Object-Oriented Languages
- •12.4 Support for Object-Oriented Programming in Smalltalk
- •12.5 Support for Object-Oriented Programming in C++
- •12.6 Support for Object-Oriented Programming in Objective-C
- •12.7 Support for Object-Oriented Programming in Java
- •12.8 Support for Object-Oriented Programming in C#
- •12.9 Support for Object-Oriented Programming in Ada 95
- •12.10 Support for Object-Oriented Programming in Ruby
- •12.11 Implementation of Object-Oriented Constructs
- •Summary
- •Programming Exercises
- •13.1 Introduction
- •13.2 Introduction to Subprogram-Level Concurrency
- •13.3 Semaphores
- •13.4 Monitors
- •13.5 Message Passing
- •13.6 Ada Support for Concurrency
- •13.7 Java Threads
- •13.8 C# Threads
- •13.9 Concurrency in Functional Languages
- •13.10 Statement-Level Concurrency
- •Summary
- •Review Questions
- •Problem Set
- •14.1 Introduction to Exception Handling
- •14.2 Exception Handling in Ada
- •14.3 Exception Handling in C++
- •14.4 Exception Handling in Java
- •14.5 Introduction to Event Handling
- •14.6 Event Handling with Java
- •14.7 Event Handling in C#
- •Review Questions
- •Problem Set
- •15.1 Introduction
- •15.2 Mathematical Functions
- •15.3 Fundamentals of Functional Programming Languages
- •15.4 The First Functional Programming Language: LISP
- •15.5 An Introduction to Scheme
- •15.6 Common LISP
- •15.8 Haskell
- •15.10 Support for Functional Programming in Primarily Imperative Languages
- •15.11 A Comparison of Functional and Imperative Languages
- •Review Questions
- •Problem Set
- •16.1 Introduction
- •16.2 A Brief Introduction to Predicate Calculus
- •16.3 Predicate Calculus and Proving Theorems
- •16.4 An Overview of Logic Programming
- •16.5 The Origins of Prolog
- •16.6 The Basic Elements of Prolog
- •16.7 Deficiencies of Prolog
- •16.8 Applications of Logic Programming
- •Review Questions
- •Programming Exercises
- •Bibliography
- •Index

14.3 Exception Handling in C++ |
643 |
one of them propagates an exception, it is impossible to determine which one raised the exception.
The problems of Ada’s exception handling are discussed in Romanovsky and Sandén (2001).
14.3 Exception Handling in C++
The exception handling of C++ was accepted by the ANSI C++ standardization committee in 1990 and subsequently found its way into C++ implementations. The design is based in part on the exception handling of CLU, Ada, and ML. One major difference between the exception handling of C++ and that of Ada is the absence of predefined exceptions in C++ (other than in its standard libraries). Thus, in C++, exceptions are user or library defined and explicitly raised.
14.3.1Exception Handlers
In Section 14.2, we saw that Ada uses program units or blocks to specify the scope for exception handlers. C++ uses a special construct that is introduced with the reserved word try for this purpose. A try construct includes a compound statement called the try clause and a list of exception handlers. The compound statement defines the scope of the following handlers. The general form of this construct is
try {
//** Code that might raise an exception
}
catch(formal parameter) {
//** A handler body
}
...
catch(formal parameter) {
//** A handler body
}
Each catch function is an exception handler. A catch function can have only a single formal parameter, which is similar to a formal parameter in a function definition in C++, including the possibility of it being an ellipsis (...). A handler with an ellipsis formal parameter is the catch-all handler; it is enacted for any raised exception if no appropriate handler was found. The formal parameter also can be a naked type specifier, such as float, as in a function prototype. In such a case, the only purpose of the formal parameter is to make the handler uniquely identifiable. When information about the exception is to be passed to the handler, the formal parameter includes a variable name that is used for that purpose. Because the class of the parameter can be

644 |
Chapter 14 Exception Handling and Event Handling |
any user-defined class, the parameter can include as many data members as are necessary. Binding exceptions to handlers is discussed in Section 14.3.2.
In C++, exception handlers can include any C++ code.
14.3.2Binding Exceptions to Handlers
C++ exceptions are raised only by the explicit statement throw, whose general form in EBNF is
throw [expression];
The brackets here are metasymbols used to specify that the expression is optional. A throw without an operand can appear only in a handler. When it appears there, it reraises the exception, which is then handled elsewhere. This effect is exactly as with Ada.
The type of the throw expression selects the particular handler, which of course must have a “matching” type formal parameter. In this case, matching means the following: A handler with a formal parameter of type T, const T, T& (a reference to an object of type T), or const T& matches a throw with an expression of type T. In the case where T is a class, a handler whose parameter is type T or any class that is an ancestor of T matches. There are more complicated situations in which a throw expression matches a formal parameter, but they are not described here.
An exception raised in a try clause causes an immediate end to the execution of the code in that try clause. The search for a matching handler begins with the handlers that immediately follow the try clause. The matching process is done sequentially on the handlers until a match is found. This means that if any other match precedes an exactly matching handler, the exactly matching handler will not be used. Therefore, handlers for specific exceptions are placed at the top of the list, followed by more generic handlers. The last handler is often one with an ellipsis (...) formal parameter, which matches any exception. This would guarantee that all exceptions were caught.
If an exception is raised in a try clause and there is no matching handler associated with that try clause, the exception is propagated. If the try clause is nested inside another try clause, the exception is propagated to the handlers associated with the outer try clause. If none of the enclosing try clauses yields a matching handler, the exception is propagated to the caller of the function in which it was raised. If the call to the function was not in a try clause, the exception is propagated to that function’s caller. If no matching handler is found in the program through this propagation process, the default handler is called. This handler is further discussed in Section 14.3.4.
14.3.3Continuation
After a handler has completed its execution, control flows to the first statement following the try construct (the statement immediately after the last handler in the sequence of handlers of which it is an element). A handler may reraise

14.3 Exception Handling in C++ |
645 |
an exception, using a throw without an expression, in which case that exception is propagated.
14.3.4Other Design Choices
In terms of the design issues summarized in Section 14.1.2, the exception handling of C++ is simple. There are only user-defined exceptions, and they are not specified (though they might be declared as new classes). There is a default exception handler, unexpected, whose only action is to terminate the program. This handler catches all exceptions not caught by the program. It can be replaced by a user-defined handler. The replacement handler must be a function that returns void and takes no parameters. The replacement function is set by assigning its name to set_terminate. Exceptions cannot be disabled.
A C++ function can list the types of the exceptions (the types of the throw expressions) that it could raise. This is done by attaching the reserved word throw, followed by a parenthesized list of these types, to the function header. For example,
int fun() throw (int, char *) { ... }
specifies that the function fun could raise exceptions of type int and char * but no others. The purpose of the throw clause is to notify users of the function what exceptions might be raised by the function. The throw clause is in effect a contract between the function and its callers. It guarantees that no other exception will be raised in the function. If the function does throw some unlisted exception, the program will be terminated. Note that the compiler ignores throw clauses.
If the types in the throw clause are classes, then the function can raise any exception that is derived from the listed classes. If a function header has a throw clause and raises an exception that is not listed in the throw clause and is not derived from a class listed there, the default handler is called. Note that this error cannot be detected at compile time. The list of types in the list may be empty, meaning that the function will not raise any exceptions. If there is no throw specification on the header, the function can raise any exception. The list is not part of the function’s type.
If a function overrides a function that has a throw clause, the overriding function cannot have a throw clause with more exceptions than the overridden function.
Although C++ has no predefined exceptions, the standard libraries define and throw exceptions, such as out_of_range, which can be thrown by library container classes, and overflow_error, which can be thrown by math library functions.
14.3.5An Example
The following example has the same intent and use of exception handling as the Ada program shown in Section 14.2.5. It produces a distribution of input grades by using an array of counters for 10 categories. Illegal grades

646 |
Chapter 14 Exception Handling and Event Handling |
are detected by checking for invalid subscripts used in incrementing the selected counter.
//Grade Distribution
//Input: A list of integer values that represent
//grades, followed by a negative number
//Output: A distribution of grades, as a percentage for
//each of the categories 0-9, 10-19, ...,
//90-100.
#include <iostream>
int main() { //* Any exception can be raised int new_grade,
index, limit_1, limit_2,
freq[10] = {0,0,0,0,0,0,0,0,0,0};
// The exception definition to deal with the end of data class NegativeInputException {
public:
NegativeInputException() { //* Constructor cout << "End of input data reached" << endl;
}//** end of constructor
}//** end of NegativeInputException class
try {
while (true) {
cout << "Please input a grade" << endl;
if ((cin >> new_grade) < 0) //* End of data throw NegativeInputException();
index = new_grade / 10; {try {
if (index > 9) throw new_grade;
freq[index]++;
}//* end of inner try compound
catch(int grade) { //* Handler for index errors if (grade == 100)
freq[9]++;
else
cout << "Error -- new grade: " << grade
<<" is out of range" << endl;
}//* end of catch(int grade)
}//* end of the block for the inner try-catch pair
}//* end of while (1)
}//* end of outer try block