
- •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

556 |
Chapter 12 Support for Object-Oriented Programming |
example, suppose the outer class creates an instance of the inner class with the following statement:
myInner = this.new Inner();
Then, if the inner class defines a variable named sum, it can be referenced in the outer class as myInner.sum.
An instance of a nested class can only exist within an instance of its nesting class. Nested classes can also be anonymous. Anonymous nested classes have complex syntax but are really only an abbreviated way to define a class that is used from just one location. An example of an anonymous nested class appears in Chapter 14.
A local nested class is defined in a method of its nesting class. Local nested classes are never defined with an access specifier (private or public). Their scope is always limited to their nesting class. A method in a local nested class can access the variables defined in its nesting class and the final variables defined in the method in which the local nested class is defined. The members of a local nested class are visible only in the method in which the local nested class is defined.
12.7.5Evaluation
Java’s design for supporting object-oriented programming is similar to that of C++, but it employs more consistent adherence to object-oriented principles. Java does not allow parentless classes and uses dynamic binding as the “normal” way to bind method calls to method definitions. This, of course, increases execution time slightly over languages in which many method bindings are static. At the time this design decision was made, however, most Java programs were interpreted, so interpretation time made the extra binding time insignificant. Access control for the contents of a class definition are rather simple when compared with the jungle of access controls of C++, ranging from derivation controls to friend functions. Finally, Java uses interfaces to provide a form of support for multiple inheritance, which does not have all of the drawbacks of actual multiple inheritance.
12.8 Support for Object-Oriented Programming in C#
C#’s support for object-oriented programming is similar to that of Java.
12.8.1General Characteristics
C# includes both classes and structs, with the classes being very similar to Java’s classes and the structs being somewhat less powerful stack-dynamic constructs. One important difference is that structs are value types; that is, they are stack

12.8 Support for Object-Oriented Programming in C# |
557 |
dynamic. This could cause the problem of object slicing, but this is prevented by the restriction that structs cannot be subclassed. More details of how C# structs differ from its classes appear in Chapter 11.
12.8.2Inheritance
C# uses the syntax of C++ for defining classes. For example,
public class NewClass : ParentClass { ... }
A method inherited from the parent class can be replaced in the derived class by marking its definition in the subclass with new. The new method hides the method of the same name in the parent class to normal access. However, the parent class version can still be called by prefixing the call with base. For example,
base.Draw();
C#’s support for interfaces is the same as that of Java.
12.8.3Dynamic Binding
To allow dynamic binding of method calls to methods in C#, both the base method and its corresponding methods in derived classes must be specially marked. The base class method must be marked with virtual, as in C++. To make clear the intent of a method in a subclass that has the same name and protocol as a virtual method in an ancestor class, C# requires that such methods be marked override if they are to override the parent class virtual method.9 For example, the C# version of the C++ Shape class that appears in Section 12.5.3 is as follows:
public class Shape {
public virtual void Draw() { ... }
...
}
public class Circle : Shape {
public override void Draw() { ... }
...
}
public class Rectangle : Shape { public override void Draw() { ... }
...
}
public class Square : Rectangle {
9. Recall that this can be specified in Java with the annotation @Override.

558 |
Chapter 12 Support for Object-Oriented Programming |
public override void Draw() { ... }
...
}
C# includes abstract methods similar to those of C++, except that they are specified with different syntax. For example, the following is a C# abstract method:
abstract public void Draw();
A class that includes at least one abstract method is an abstract class, and every abstract class must be marked abstract. Abstract classes cannot be instantiated. It follows that any subclass of an abstract class that will be instantiated must implement all abstract methods that it inherits.
As with Java, all C# classes are ultimately derived from a single root class, Object. The Object class defines a collection of methods, including ToString, Finalize, and Equals, which are inherited by all C# types.
12.8.4Nested Classes
A C# class that is directly nested in a class behaves like a Java static nested class (which is like a nested class in C++). Like C++, C# does not support nested classes that behave like the nonstatic nested classes of Java.
12.8.5Evaluation
Because C# is the most recently designed C-based object-oriented language, one should expect that its designers learned from their predecessors and duplicated the successes of the past and remedied some of the problems. One result of this, coupled with the few problems with Java, is that the differences between C#’s support for object-oriented programming and that of Java are relatively minor. The availability of structs in C#, which Java does not have, can be considered an improvement. Like that of Java, C#’s support for objectoriented programming is simpler than that of C++, which many consider an improvement.
12.9 Support for Object-Oriented Programming in Ada 95
Ada 95 was derived from Ada 83, with some significant extensions. This section presents a brief look at the extensions that were designed to support objectoriented programming. Because Ada 83 already included constructs for building abstract data types, the necessary additional features for Ada 95 were those for supporting inheritance and dynamic binding. The design objectives of Ada 95 were to include minimal changes to the type and package structures of Ada 83 and retain as much static type checking as possible. Note that object-oriented

12.9 Support for Object-Oriented Programming in Ada 95 |
559 |
programming in Ada 95 is complicated and that this section includes only a brief and incomplete description of it.
12.9.1General Characteristics
Ada 95 classes are a new category of types called tagged types, which can be either records or private types. They are defined in packages, which allows them to be separately compiled. Tagged types are so named because each object of a tagged type implicitly includes a system-maintained tag that indicates its type. The subprograms that define the operations on a tagged type appear in the same declaration list as the type declaration. Consider the following example:
with Ada.Strings.Unbounded; use Ada.Strings.Unbounded; package Person_Pkg is
type Person is tagged private; procedure Display(P : in Person);
private
type Person is tagged
record
Name : Unbounded_String;
Address : Unbounded_String;
Age : Integer;
end record;
end Person_Pkg;
This package defines the type Person, which is useful by itself and can also serve as the parent class of derived classes.
Unlike C++, there is no implicit calling of constructor or destructor subprograms in Ada 95. These subprograms can be written, but they must be explicitly called by the programmer.
12.9.2Inheritance
Ada 83 supports only a narrow form of inheritance with its derived types and subtypes. In both of these, a new type can be defined on the basis of an existing type. The only modification allowed is to restrict the range of values of the new type. This is not the kind of full inheritance required for object-oriented programming, which is supported by Ada 95.
Derived types in Ada 95 are based on tagged types. New entities are added to the inherited entities by placing them in a record definition. Consider the following example:
with Person_Pkg; use Person_Pkg;
package Student_Pkg is
type Student is new Person with

560 |
Chapter 12 Support for Object-Oriented Programming |
record
Grade_Point_Average : Float;
Grade_Level : Integer;
end record;
procedure Display(St : in Student);
end Student_Pkg;
In this example, the derived type Student is defined to have the entities of its parent class, Person, along with the new entities Grade_Point_Average and Grade_Level. It also redefines the procedure Display. This new class is defined in a separate package to allow it to be changed without requiring recompilation of the package containing the definition of the parent type.
This inheritance mechanism does not allow one to prevent entities of the parent class from being included in the derived class. Consequently, derived classes can only extend parent classes and are therefore subtypes. However, child library packages, which are discussed briefly below, can be used to define subclasses that are not subtypes.
Suppose we have the following definitions:
P1 : Person;
S1 : Student;
Fred : Person := (To_Unbounded_String("Fred"),
To_Unbounded_String("321 Mulberry
Lane"), 35);
Freddie : Student :=
(To_Unbounded_String("Freddie"),
To_Unbounded_String("725 Main St."), 20, 3.25, 3);
Because Student is a subtype of Person, the assignment
P1 := Freddie;
should be legal, and it is. The Grade_Point_Average and Grade_Level entities of Freddie are simply ignored in the required coercion. This is another example of object slicing.
The obvious question now is whether an assignment in the opposite direction is legal; that is, can we assign a Person to a Student? In Ada 95, this action is legal in a form that includes the entities in the subclass. In our example, the following is legal:
S1 := (Fred, 3.05, 2);
Ada 95 does not provide multiple inheritance. Although generic classes and multiple inheritance are only distantly related concepts, there is a way to achieve an effect similar to multiple inheritance using generics. However, it is not as elegant as the C++ approach, and it is not discussed here.

12.9 Support for Object-Oriented Programming in Ada 95 |
561 |
12.9.3Dynamic Binding
Ada 95 provides both static binding and dynamic binding of procedure calls to procedure definitions in tagged types. Dynamic binding is forced by using a classwide type, which represents all of the types in a class hierarchy rooted at a particular type. Every tagged type implicitly has a classwide type. For a tagged type T, the classwide type is specified with T'class. If T is a tagged type, a variable of type T'class can store an object of type T or any type derived from T.
Consider again the Person and Student classes defined in Section 12.9.2. Suppose we have a variable of type Person'class, Pcw, which sometimes references a Person object and sometimes references a Student object. Furthermore, suppose we want to display the object referenced by Pcw, regardless of whether it is referencing a Person object or a Student object. This result requires the call to Display to be dynamically bound to the correct version of Display. We could use a new procedure that takes the Person type parameter and sends it to Display. Following is such a procedure:
procedure Display_Any_Person(P: in Person) is
begin
Display(P);
end Display_Any_Person;
This procedure can be called with both of the following calls:
with Person_Pkg; use Person_Pkg; with Student_Pkg; use Student_Pkg; P : Person;
S : Student;
Pcw : Person'class;
...
Pcw := P;
Display_Any_Person(Pcw); -- call the Display in Person Pcw := S;
Display_Any_Person(Pcw); -- call the Display in Student
Ada 95+ also supports polymorphic pointers. They are defined to have the classwide type, as in
type Any_Person_Ptr is access Person'class;
Purely abstract base types can be defined in Ada 95+ by including the reserved word abstract in the type definitions and the subprogram definitions. Furthermore, the subprogram definitions cannot have bodies. Consider this example:
package Base_Pkg is
type T is abstract tagged null record;

562 |
Chapter 12 Support for Object-Oriented Programming |
procedure Do_It (A : T) is abstract;
end Base_Pkg;
12.9.4Child Packages
Packages can be nested directly in other packages, in which case they are called child packages. One potential problem with this design is that if a package has a significant number of child packages and they are large, the nesting package becomes too large to be an effective compilation unit. The solution is relatively simple: Child packages are allowed to be physically separate units (files) that are separately compilable, in which case they are called child library packages.
A child package is declared to be private by preceding the reserved word package with the reserved word private. The logical position of a private child package is at the beginning of the declarations in the specification package of the nesting package. The declarations of the private child package are not visible to the nesting package body, unless the nesting package includes a with clause with the child’s name.
One important characteristic of a child package is that even the private parts of its parent are visible to it. Child packages provide an alternative to class derivation, because of this visibility of the parent entities. So, the private parts of the parent package are like protected members in a parent class where a child package is used to extend a class.
Child library packages can be added at any time to a program. They do not require recompilation of the parent package or clients of the parent package.
Child library packages can be used in place of the friend definitions in C++. For example, if a subprogram must be written that can access the members of two different classes, the parent package can define one of the classes and the child package can define the other. Then, a subprogram in the child package can access the members of both. Furthermore, in C++ if the need for a friend is not known when a class is defined, it will need to be changed and recompiled when such a need is discovered. In Ada 95+, new classes in new child packages can be defined without disturbing the parent package, because every name defined in the parent package is visible in the child package.
12.9.5Evaluation
Ada offers complete support for object-oriented programming, although users of other object-oriented languages may find that support to be both weak and somewhat complex. Although packages can be used to build abstract data types, they are actually more generalized encapsulation constructs. Unless child library packages are used, there is no way to restrict inheritance, in which case all subclasses are subtypes. This form of access restriction is limited in comparison to that offered by C++, Java, and C#.
C++ clearly offers a better form of multiple inheritance than Ada 95. However, the use of child library units to control access to the entities of