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

11.4 Language Examples |
479 |
name must have external visibility, the type representation must be hidden. The type representation and the definitions of the subprograms that implement the operations may appear inside or outside this syntactic unit.
Few, if any, general built-in operations should be provided for objects of abstract data types, other than those provided with the type definition. There simply are not many operations that apply to a broad range of abstract data types. Among these are assignment and comparisons for equality and inequality. If the language does not allow users to overload assignment, the assignment operation must be included in the abstraction. Comparisons for equality and inequality should be predefined in the abstraction in some cases but not in others. For example, if the type is implemented as a pointer, equality may mean pointer equality, but the designer may want it to mean equality of the structures referenced by the pointers.
Some operations are required by many abstract data types, but because they are not universal, they often must be provided by the designer of the type. Among these are iterators, accessors, constructors, and destructors. Iterators were discussed in Chapter 8. Accessors provide a form of access to data that is hidden from direct access by clients. Constructors are used to initialize parts of newly created objects. Destructors are often used to reclaim heap storage that may be used by parts of abstract data type objects in languages that do not do implicit storage reclamation.
As stated earlier, the enclosure for an abstract data type defines a single data type and its operations. Many contemporary languages, including C++, Objective-C, Java, and C#, directly support abstract data types. One alternative approach is to provide a more generalized encapsulation construct that can define any number of entities, any of which can be selectively specified to be visible outside the enclosing unit. Ada uses this approach. These enclosures are not abstract data types but rather are generalizations of abstract data types. As such, they can be used to define abstract data types. Although we discuss Ada’s encapsulation construct in this section, we treat it as a minimal encapsulation for single data types. Generalized encapsulations are the topic of Section 11.6.
So, the first design issue for abstract data types is the form of the container for the interface to the type. The second design issue is whether abstract data types can be parameterized. For example, if the language supports parameterized abstract data types, one could design an abstract data type for some structure that could store elements of any type. Parameterized abstract data types are discussed in Section 11.5. The third design issue is what access controls are provided and how such controls are specified. Finally, the language designer must decide whether the specification of the type is physically separate from its implementation (or whether that is a developer choice).
11.4 Language Examples
The concept of data abstraction had its origins in SIMULA 67, although that language did not provide complete support for abstract data types, because it did not include a way to hide implementation details. In this section, we describe the support for data abstraction provided by Ada, C++, Objective-C, Java, C#, and Ruby.

inter view
C++: Its Birth, Its Ubiquitousness,
and Common Criticisms
B J A R N E S T R O U S T R U P
Bjarne Stroustrup is the designer and original implementer of C++ and the author of The C++ Programming Language and The Design and Evolution of C++. His research interests include distributed systems, simulation, design, programming, and programming languages. Dr. Stroustrup is the College of Engineering Professor in Computer Science at Texas A&M University. He is actively involved in the ANSI/ISO
standardization of C++. After more than two decades at AT&T, he retains a link with AT&T Labs, doing research as a member of the Information and Software Systems Research Lab. He is an ACM Fellow, an AT&T Bell Laboratories Fellow, and an AT&T Fellow. In 1993, Stroustrup received the ACM Grace Murray Hopper Award “for his early work laying the foundations for the C++ programming language. Based on the foundations and Dr. Stroustrup’s continuing efforts, C++ has become one of the most influential programming languages in the history of computing.”
A BRIEF HISTORY OF YOU AND COMPUTING
What were you working on, and where, before you joined Bell Labs in the early 1980s? At Bell Labs, I was doing research in the general area of distributed systems. I joined in 1979. Before that, I was finishing my Ph.D. in that field in Cambridge University.
Did you immediately start on “C with Classes” (which would later become C++)? I worked on a few projects related to distributed computing before starting on C with Classes and during the development of that and of C++. For example, I was trying to find a way to distribute the UNIX kernel across several computers and helped a lot of projects build simulators.
Was it an interest in mathematics that got you into this profession? I signed up for a degree in “mathematics with computer science” and my master’s degree is officially a math degree. I—wrongly— thought that computing was some kind of applied math. I did a couple of years of math and rate myself a poor mathematician, but that’s still much better than not knowing math. At the time I signed up, I had never even seen a computer. What I love about computing is the programming rather than the more mathematical fields.
DISSECTING A SUCCESSFUL LANGUAGE
I’d like to work backward, listing some items I think make C++ ubiquitous, and get your reaction. It’s “open source,” nonproprietary, and standardized by ANSI/ISO. The ISO C++ standard is important. There are many independently developed and evolving C++ implementations. Without a standard for them to adhere to and a standards process to help coordinate the evolution of C++, a chaos of dialects would erupt.
It is also important that there are both open-source and commercial implementations available. In addition, for many users, it is crucial that the standard provides a measure of protection from manipulation by implementation providers.
The ISO standards process is open and democratic. The C++ committee rarely meets with fewer than 50 people present and typically more than eight nations are represented at each meeting. It is not just a vendors’ forum.
It’s ideal for systems programming (which, at the time C++ was born, was the largest sector of the market developing code).
Yes, C++ is a strong contender for any systemsprogramming project. It is also effective for embedded
480

systems programming, which is currently the fastestgrowing sector. Yet another growth area for C++
is high-performance numeric/engineering/scientific programming.
Its object-oriented nature and inclusion of classes/libraries make programming more efficient and transparent. C++ is a multiparadigm programming language. That is, it supports several fundamental styles of programming (including objectoriented programming) and combinations of those styles. When used well, this leads to cleaner, more flexible, and more efficient libraries than can be provided using just one paradigm. The C++ standard library containers and algorithms, which is basically a generic programming framework, is an example. When used together with (object-oriented) class hierarchies, the result is an unsurpassed combination of type safety, efficiency, and flexibility.
Its incubation in the AT&T development environment. AT&T Bell Labs provided an environment that was crucial for C++’s development. The labs were
an exceptionally rich source of challenging problems and a uniquely supportive environment for practical research. C++ emerged from the same research lab as C did and benefited from the same intellectual tradition, experience, and exceptional people. Throughout, AT&T supported the standardization of C++. However, C++ was not the beneficiary of a massive marketing campaign, like many modern languages. That’s simply not the way the labs work.
Did I miss anything on your top list? Undoubtedly.
Now, let me paraphrase from the C++ critiques and get your reactions: It’s huge/unwieldy. The “hello world” problem is 10 times larger in C++ than in C. C++ is certainly not a small language, but then few modern languages are. If a language is
small, you tend to need huge libraries to get work done and often have to rely on conventions and extensions. I prefer to have key parts of the inevitable complexity in the language where it can be seen, taught, and effectively standardized rather than hidden elsewhere in a system. For most purposes, I don’t consider C++ unwieldy. The C++ “hello world” program isn’t larger
than its C equivalent on my machine, and it shouldn’t be on yours.
In fact, the object code for the C++ version of the “hello world” program is smaller than the C version on my machine. There is no language reason why the one version should be larger than the other. It is all an issue of how the implementor organized the libraries. If one version is significantly larger than the other, report the problem to the implementor of the larger version.
It’s tougher to program in C++ (compared with C). (Something the critics say.) Even you once admitted it, saying something about shooting yourself in the foot with C versus C++. Yes, I did say something along the lines of “C makes it easy to shoot yourself in the foot; C++ makes it harder, but when you do, C++ blows your whole leg off.” What people tend to miss is that what I said about C++ is to a varying extent true for all powerful languages. As you protect people from simple dangers, they get themselves into new and less obvious problems. Someone who avoids the simple problems may simply be heading for a not- so-simple one. One problem with very supporting and protective environments is that the hard problems may be discovered too late or be too hard to remedy once discovered. Also, a rare problem is harder to find than a frequent one because you don’t suspect it.
It’s appropriate for embedded systems of today but not for the Internet software of today. C++ is suitable for embedded systems today. It is also suitable—and widely used—for “Internet software” today. For example, have a look at my “C++ applications” Web page. You’ll notice that some of the major Web service providers, such as Amazon, Adobe, Google, Quicken, and Microsoft, critically rely on C++. Gaming is a related area in which you find heavy C++ use.
Did I miss another one that you get a lot? Sure.
481

482 |
Chapter 11 |
Abstract Data Types and Encapsulation Constructs |
11.4.1Abstract Data Types in Ada
Ada provides an encapsulation construct that can be used to define a single abstract data type, including the ability to hide its representation. Ada 83 was one of the first languages to offer full support for abstract data types.
11.4.1.1 Encapsulation
The encapsulating constructs in Ada are called packages. A package can have two parts, each of which is also is called a package. These are called the package specification, which provides the interface of the encapsulation (and perhaps more), and the body package, which provides the implementation of most, if not all, of the entities named in the associated package specification. Not all packages have a body part (packages that encapsulate only types and constants do not have or need bodies).
A package specification and its associated body package share the same name. The reserved word body in a package header identifies it as being a body package. A package specification and its body package may be compiled separately, provided the package specification is compiled first. Client code can also be compiled before the body package is compiled or even written, for that matter. This means that once the package specification is written, work can begin on both the client code and the body package.
11.4.1.2 Information Hiding
The designer of an Ada package that defines a data type can choose to make the type entirely visible to clients or provide only the interface information. Of course, if the representation is not hidden, then the defined type is not an abstract data type. There are two approaches to hiding the representation from clients in the package specification. One is to include two sections in the package specification—one in which entities are visible to clients and one that hides its contents. For an abstract data type, a declaration appears in the visible part of the specification, providing only the name of the type and the fact that its representation is hidden. The representation of the type appears in a part of the specification called the private part, which is introduced by the reserved word private. The private clause is always at the end of the package specification. The private clause is visible to the compiler but not to client program units.
The second way to hide the representation is to define the abstract data type as a pointer and provide the pointed-to structure’s definition in the body package, whose entire contents are hidden from clients.
Types that are declared to be private are called private types. Private data types have built-in operations for assignment and comparisons for equality and inequality. Any other operation must be declared in the package specification that defined the type.
The reason that a type’s representation appears in the package specification at all has to do with compilation issues. Client code can see only the package

11.4 Language Examples |
483 |
specification (not the body package), but the compiler must be able to allocate objects of the exported type when compiling the client. Furthermore, the client is compilable when only the package specification for the abstract data type has been compiled and is present. Therefore, the compiler must be able to determine the size of an object from the package specification. So, the representation of the type must be visible to the compiler but not to the client code. This is exactly the situation specified by the private clause in a package specification.
An alternative to private types is a more restricted form: limited private types. Nonpointer limited private types are described in the private section of a package specification, as are nonpointer private types. The only syntactic difference is that limited private types are declared to be limited private in the visible part of the package specification. The semantic difference is that objects of a type that is declared limited private have no built-in operations. Such a type is useful when the usual predefined operations of assignment and comparison are not meaningful or useful. For example, assignment and comparison are rarely used for stacks.
11.4.1.3 An Example
The following is the package specification for a stack abstract data type:
package Stack_Pack is
-- The visible entities, or public interface type Stack_Type is limited private; Max_Size : constant := 100;
function Empty(Stk : in Stack_Type) return Boolean; procedure Push(Stk : in out Stack_Type;
Element : in Integer); procedure Pop(Stk : in out Stack_Type);
function Top(Stk : in Stack_Type) return Integer; -- The part that is hidden from clients
private
type List_Type is array (1..Max_Size) of Integer; type Stack_Type is
record
List : List_Type;
Topsub : Integer range 0..Max_Size := 0; end record;
end Stack_Pack;
Notice that no create or destroy operations are included, because they are not necessary.
The body package for Stack_Pack is as follows:
with Ada.Text_IO; use Ada.Text_IO;
package body Stack_Pack is

484 |
Chapter 11 |
Abstract Data Types and Encapsulation Constructs |
function Empty(Stk: in Stack_Type) return Boolean is
begin
return Stk.Topsub = 0;
end Empty;
procedure Push(Stk : in out Stack_Type; Element : in Integer) is
begin
if Stk.Topsub >= Max_Size then
Put_Line("ERROR - Stack overflow");
else
Stk.Topsub := Stk.Topsub + 1;
Stk.List(Topsub) := Element;
end if;
end Push;
procedure Pop(Stk : in out Stack_Type) is
begin
if Empty(Stk)
then Put_Line("ERROR - Stack underflow"); else Stk.Topsub := Stk.Topsub - 1;
end if; end Pop;
function Top(Stk : in Stack_Type) return Integer is begin
if Empty(Stk)
then Put_Line("ERROR - Stack is empty"); else return Stk.List(Stk.Topsub);
end if;
end Top;
end Stack_Pack;
The first line of the code of this body package contains two clauses: a with and a use. The with clause makes the names defined in external packages visible; in this case Ada.Text_IO, which provides functions for input and output of text. The use clause eliminates the need for explicit qualification of the references to entities from the named package. The issues of access to external encapsulations and name qualifications are further discussed in Section 11.7.
The body package must have subprogram definitions with headings that match the subprogram headings in the associated package specification. The package specification promises that these subprograms will be defined in the associated body package.
The following procedure, Use_Stacks, is a client of package Stack_Pack. It illustrates how the package might be used.

11.4 Language Examples |
485 |
with Stack_Pack; use Stack_Pack;
procedure Use_Stacks is
Topone : Integer;
Stack : Stack_Type; -- Creates a Stack_Type object
begin
Push(Stack, 42);
Push(Stack, 17);
Topone := Top(Stack);
Pop(Stack);
...
end Use_Stacks;
A stack is a silly example for most contemporary languages, because support for stacks is included in their standard class libraries. However, stacks provide a simple example we can use to allow comparisons of the languages discussed in this section.
11.4.1.4 Evaluation
Ada, along with Modula-2, was the first commercial language to support abstract data types.2 Although Ada’s design of abstract data types may seem complicated and repetitious, it clearly provides what is necessary.
11.4.2Abstract Data Types in C++
C++, which was first released in 1985, was created by adding features to C. The first important additions were those to support object-oriented programming. Because one of the primary components of object-oriented programming is abstract data types, C++ obviously is required to support them.
While Ada provides an encapsulation that can be used to simulate abstract data types, C++ provides two constructs that are very similar to each other, the class and the struct, which more directly support abstract data types. Because structs are most commonly used when only data is included, we do not discuss them further here.
C++ classes are types; as stated previously, Ada packages are more generalized encapsulations that can define any number of types. A program unit that gains visibility to an Ada package can access any of its public entities directly by their names. A C++ program unit that declares an instance of a class can also access any of the public entities in that class, but only through an instance of the class. This is a cleaner and more direct way to provide abstract data types.
2.The language CLU, which was an academic research language, rather than a commercial language, was the first to support abstract data types.

486 |
Chapter 11 |
Abstract Data Types and Encapsulation Constructs |
11.4.2.1 Encapsulation
The data defined in a C++ class are called data members; the functions (methods) defined in a class are called member functions. Data members and member functions appear in two categories: class and instance. Class members are associated with the class; instance members are associated with the instances of the class. In this chapter, only the instance members of a class are discussed. All of the instances of a class share a single set of member functions, but each instance has its own set of the class’s data members. Class instances can be static, stack dynamic, or heap dynamic. If static or stack dynamic, they are referenced directly with value variables. If heap dynamic, they are referenced through pointers. Stack dynamic instances of classes are always created by the elaboration of an object declaration. Furthermore, the lifetime of such a class instance ends when the end of the scope of its declaration is reached. Heap dynamic class instances are created with the new operator and destroyed with the delete operator. Both stackand heap-dynamic classes can have pointer data members that reference heap dynamic data, so that even though a class instance is stack dynamic, it can include data members that reference heap dynamic data.
A member function of a class can be defined in two distinct ways: The complete definition can appear in the class, or only in its header. When both the header and the body of a member function appear in the class definition, the member function is implicitly inlined. Recall that this means that its code is placed in the caller’s code, rather than requiring the usual call and return linkage process. If only the header of a member function appears in the class definition, its complete definition appears outside the class and is separately compiled. The rationale for allowing member functions to be inlined was to save function call overhead in real-time applications, in which run-time efficiency is of utmost importance. The downside of inlining member functions is that it clutters the class definition interface, resulting in a reduction in readability.
Placing member function definitions outside the class definition separates specification from implementation, a common goal of modern programming.
11.4.2.2 Information Hiding
A C++ class can contain both hidden and visible entities (meaning they are either hidden from or visible to clients of the class). Entities that are to be hidden are placed in a private clause, and visible, or public, entities appear in a public clause. The public clause therefore describes the interface to class instances.3
3.There is also a third category of visibility, protected, which is discussed in the context of inheritance in Chapter 12.

11.4 Language Examples |
487 |
11.4.2.3 Constructors and Destructors
C++ allows the user to include constructor functions in class definitions, which are used to initialize the data members of newly created objects. A constructor may also allocate the heap-dynamic data that are referenced by the pointer members of the new object. Constructors are implicitly called when an object of the class type is created. A constructor has the same name as the class whose objects it initializes. Constructors can be overloaded, but of course each constructor of a class must have a unique parameter profile.
A C++ class can also include a function called a destructor, which is implicitly called when the lifetime of an instance of the class ends. As stated earlier, stack-dynamic class instances can contain pointer members that reference heap-dynamic data. The destructor function for such an instance can include a delete operator on the pointer members to deallocate the heap space they reference. Destructors are often used as a debugging aid, in which case they simply display or print the values of some or all of the object’s data members before those members are deallocated. The name of a destructor is the class’s name, preceded by a tilde (~).
Neither constructors nor destructors have return types, and neither use return statements. Both constructors and destructors can be explicitly called.
11.4.2.4 An Example
Our examle of a C++ abstract data type is, once again, a stack:
#include <iostream.h> class Stack {
private: //** These members are visible only to other //** members and friends (see Section 11.6.4)
int *stackPtr; int maxLen; int topSub;
public: //** These members are visible to clients Stack() { //** A constructor
stackPtr = new int [100]; maxLen = 99;
topSub = -1;
}
~Stack() {delete [] stackPtr;}; //** A destructor void push(int number) {
if (topSub == maxLen)
cerr << "Error in push--stack is full\n"; else stackPtr[++topSub] = number;
}
void pop() { if (empty())

488 Chapter 11 Abstract Data Types and Encapsulation Constructs
cerr << "Error in pop--stack is empty\n"; else topSub--;
}
int top() { if (empty())
cerr << "Error in top--stack is empty\n";
else
return (stackPtr[topSub]);
}
int empty() {return (topSub == -1);}
}
We discuss only a few aspects of this class definition, because it is not necessary to understand all of the details of the code. Objects of the Stack class are stack dynamic but include a pointer that references heap-dynamic data. The Stack class has three data members—stackPtr, maxLen, and topSub—all of which are private. stackPtr is used to reference the heap-dynamic data, which is the array that implements the stack. The class also has four public member functions—push, pop, top, and empty—as well as a constructor and a destructor. All of the member function definitions are included in this class, although they could have been externally defined. Because the bodies of the member functions are included, they are all implicitly inlined. The constructor uses the new operator to allocate an array of 100 int elements from the heap. It also initializes maxLen and topSub.
The following is an example program that uses the Stack abstract data type:
void main() { int topOne;
Stack stk; //** Create an instance of the Stack class stk.push(42);
stk.push(17); topOne = stk.top(); stk.pop();
...
}
Following is a definition of the Stack class with only prototypes of the member functions. This code is stored in a header file with the .h file name extension. The definitions of the member functions follow the class definition. These use the scope resolution operator, ::, to indicate the class to which they belong. These definitions are stored in a code file with the file name extension .cpp.
// Stack.h - the header file for the Stack class #include <iostream.h>

11.4 Language Examples |
489 |
class Stack {
private: //** These members are visible only to other //** members and friends (see Section 11.6.4)
int *stackPtr; int maxLen; int topSub;
public: //** These members are visible to clients Stack(); //** A constructor
~Stack(); //** A destructor void push(int);
void pop(); int top(); int empty();
}
// Stack.cpp - the implementation file for the Stack class #include <iostream.h>
#include "Stack.h" using std::cout;
Stack::Stack() { //** A constructor stackPtr = new int [100];
maxLen = 99; topSub = -1;
}
Stack::~Stack() {delete [] stackPtr;}; //** A destructor
void Stack::push(int number) { if (topSub == maxLen)
cerr << "Error in push--stack is full\n"; else stackPtr[++topSub] = number;
}
void Stack::pop() { if (topSub == -1)
cerr << "Error in pop--stack is empty\n"; else topSub--;
}
int top() {
if (topSub == -1)
cerr << "Error in top--stack is empty\n";
else
return (stackPtr[topSub]);
}
int Stack::empty() {return (topSub == -1);}

490 |
Chapter 11 |
Abstract Data Types and Encapsulation Constructs |
11.4.2.5 Evaluation
C++ support for abstract data types, through its class construct, is similar in expressive power to that of Ada, through its packages. Both provide effective mechanisms for encapsulation and information hiding of abstract data types. The primary difference is that classes are types, whereas Ada packages are more general encapsulations. Furthermore, the package construct of Ada was designed for more than data abstraction, as discussed in Chapter 12.
11.4.3Abstract Data Types in Objective-C
As has been previously stated, Objective-C is similar to C++ in that its initial design was the C language with extensions to support object-oriented programming. One of the fundamental differences between the two is that Objective-C uses the syntax of Smalltalk for its method calls.
11.4.3.1 Encapsulation
The interface part of an Objective-C class is defined in a container called an interface with the following general syntax:
@interface class-name: parent-class { instance variable declarations
}
method prototypes
@end
The first and last lines, which begin with at signs (@), are directives.
The implementation of a class is packaged in a container naturally named implementation, which has the following syntax:
@implementation class-name method definitions
@end
As in C++, in Objective-C classes are types.
Method prototypes have the following syntax:
(+ | -)(return-type) method-name [: (formal-parameters)];
When present, the plus sign indicates that the method is a class method; a minus sign indicates an instance method. The brackets around the formal parameters indicate that they are optional. Neither the parentheses nor the colon are present when there are no parameters. As in most other languages that support object-oriented programming, all instances of an Objective-C class share a single copy of its instance methods, but each instance has its own copy of the instance data.

11.4 Language Examples |
491 |
The syntactic form of the formal parameter list is different from that of the more common languages, C, C++, Java, and C#. If there is one parameter, its type is specified in parentheses before the parameter’s name, as in the following method prototype:
-(void) meth1: (int) x;
This method’s name is meth1: (note the colon). A method with two parameters could appear as in the following example method prototype:
-(int) meth2: (int) x second: (float) y;
In this case, the method’s name is meth2:second:, although that is obviously a poorly chosen name. The last part of the name (second) could have been omitted, as in the following:
-(int) meth2: (int) x: (float) y;
In this case, the name of the method is meth2::.
Method definitions are like method prototypes except that they have a brace-delimited sequence of statements in place of the semicolon.
The syntax of a call to a method with no parameters is as follows:
[object-name method-name];
If a method takes one parameter, a colon is attached to the method name and the parameter follows. There is no other punctuation between the method name and the parameter. For example, a call to a method named add1 on the object referenced by myAdder that takes one parameter, in this case the literal 7, would appear as follows:
[myAdder add1: 7];
If a method takes two parameters and has only one part to its name, a colon follows the first parameter and the second parameter follows that. No other punctuation is used between the two parameters. If there are more parameters, this pattern is repeated. For example, if add1 takes three parameters and has no other parts to its name, it could be called with the following:
[myAdder add1: 7: 5: 3];
A method could have multiple parameters and multiple parts to its name, as in the previous example:
-(int) meth2: (int) x second: (float) y;
An example call to this method follows:
[myObject meth2: 7 second: 3.2];

492 |
Chapter 11 |
Abstract Data Types and Encapsulation Constructs |
Constructors in Objective-C are called initializers; they only provide initial values. They can be given any name, and as a result they must be explicitly called. Constructors return a reference to the new object, so their type is always a pointer to the class-name. They use a return statement that returns self, a reference to the current object.
An object is created in Objective-C by calling alloc. Typically, after calling alloc, the constructor of the class is explicitly called. These two calls can be and usually are cascaded, as in the following statement, which creates an object of Adder class with alloc and then calls its constructor, init, on the new object, and puts the address of the new object in myAdder:
Adder *myAdder = [[Adder alloc]init];
All class instances are heap dynamic and are referenced through reference variables.
C programs nearly always import a header file for input and output functions, stdio.h. In Objective-C, a header file is usually imported that has the prototypes of a variety of often required functions, including those for input and output, as well as some needed data. This is done with the following:
#import <Foundation/Foundation.h>
Importing the Foundation.h header file creates some data for the program. So, the first thing the main function should do is allocate and initialize a pool of storage for this data. This is done with the following statement:
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
Just before the return statement in main, this pool is released with a call to the drain method of the pool object, as in the following statement:
[pool drain];
11.4.3.2 Information Hiding
Objective-C uses the directives, @private and @public, to specify the access levels of the instance variables in a class definition. These are used as the reserved words public and private are used in C++. The difference is that the default in Objective-C is protected, whereas it is private in C++. Unlike most programming languages that support object-oriented programming, in Objective-C there is no way to restrict access to a method.
In Objective-C, the convention is that the name of a getter method for an instance variable is the variable’s name. The name of the setter method is the word set with the capitalized variable’s name attached. So, for a variable named sum, the getter method would be named sum and the setter method

11.4 Language Examples |
493 |
would be named setSum. Assuming that sum is an int variable, these methods could be defined as follows:
// The getter for sum -(int) sum {
return sum;
}
// The setter for sum -(void) setSum: (int) s {
sum = s;
}
If the getter and setter method for a particular variable does not impose any constraints on their actions, they can be automatically generated by the Objective-C compiler. This is done by listing the instance variables for which getters and setters are to be generated on the property directive in the interface section, as in the following:
@property int sum;
In the implementation section, the variables are listed in a synthesize directive, as in the following:
@synthesize sum;
Variables for which getters and setters are generated by the compiler are often called properties and the accessor methods are said to be synthesized.
The getters and setters of instance variables can be used in two ways, either in method calls or in dot notation, as if they were publically accessible. For example, if we have defined a getter and a setter for the variable sum, they could be used as in the following:
[myObject setSum: 100];
newSum = [myObject sum];
or as if they were publically accessible, as in the following:
myObject.sum = 100;
newSum = myObject.sum;
11.4.3.3 An Example
Following are the definitions of the interface and implementation of the stack class in Objective-C:

494Chapter 11 Abstract Data Types and Encapsulation Constructs
//stack.m - interface and implementation of a simple stack
#import <Foundation/Foundation.h>
//Interface section
@interface Stack: NSObject { int stackArray [100];
int stackPtr; int maxLen; int topSub;
}
-(void) push: (int) number; -(void) pop;
-(int) top; -(int) empty;
@end
// Implementation section
@implementation Stack -(Stack *) initWith {
maxLen = 100; topSub = -1;
stackPtr = stackArray; return self;
}
-(void) push: (int) number { if (topSub == maxLen)
NSLog(@"Error in push--stack is full"); else
stackPtr[++topSub] = number;
}
-(void) pop {
if (topSub == -1)
NSLog(@"Error in pop--stack is empty"); else
topSub--;
}
-(int) top {
if (topSub >= 0)
return stackPtr[topSub]);
else
NSLog(@"Error in top--stack is empty");

11.4 Language Examples |
495 |
}
-(int) empty {
return topSub == -1);
}
int main (int argc, char *argv[]) { int temp;
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
Stack *myStack = [[Stack alloc]initWith]; [myStack push: 5];
[myStack push: 3]; temp = [myStack top];
NSLog(@"Top element is:%i", temp); [myStack pop];
temp = [myStack top];
NSLog(@"Top element is:%i", temp); temp = [myStack top];
[myStack pop]; [myStack release]; [pool drain]; return 0;
}
@end
The output of this program is as follows:
Top element is: 3
Top element is: 5
Error in top--stack is empty
Error in pop--stack is empty
Screen output from an Objective-C program is created with a call to a method with the odd-looking name, NSLog, which takes a literal string as its parameter. Literal strings are created with an at sign (@) followed by a quoted string. If an output string includes the values of variables, the names of the variables are included as parameters in the call to NSLog. The positions in the literal string for the values are marked with format codes, for example %i for an integer and %f for a floating-point value in scientific notation, as is similar to C’s printf function.
11.4.3.4 Evaluation
The support in Objective-C for abstract data types is adequate. Some find it odd that it uses syntactic forms from two very different languages, Smalltalk (for its method calls) and C (for nearly everything else). Also, its use of

496 |
Chapter 11 |
Abstract Data Types and Encapsulation Constructs |
directives in place of language constructs to indicate class interfaces and implementation sections also differs from most other programming languages. One minor deficiency is the lack of a way to restrict access to methods. So, even methods meant only to be used inside a class are accessible to clients. Another minor deficiency is that constructors must be explicitly called, thereby requiring programmers to remember to call them, and also leading to further clutter in the client program.
11.4.4Abstract Data Types in Java
Java support for abstract data types is similar to that of C++. There are, however, a few important differences. All objects are allocated from the heap and accessed through reference variables. Methods in Java must be defined completely in a class. A method body must appear with its corresponding method header.4 Therefore, a Java abstract data type is both declared and defined in a single syntactic unit. A Java compiler can inline any method that is not overridden. Definitions are hidden from clients by declaring them to be private.
Rather than having private and public clauses in its class definitions, in Java access modifiers can be attached to method and variable definitions. If an instance variable or method does not have an access modifier, it has package access, which is discussed in Section 11.7.2.
11.4.4.1 An Example
The following is a Java class definition for our stack example:
class StackClass { private int [] stackRef; private int maxLen,
topIndex;
public StackClass() { // A constructor stackRef = new int [100];
maxLen = 99; topIndex = -1;
}
public void push(int number) { if (topIndex == maxLen)
System.out.println("Error in push—stack is full"); else stackRef[++topIndex] = number;
}
public void pop() { if (empty())
System.out.println("Error in pop—stack is empty");
4.Java interfaces are an exception to this—an interface has method headers but cannot include their bodies.

11.4 Language Examples |
497 |
else --topIndex;
}
public int top() { if (empty()) {
System.out.println("Error in top—stack is empty"); return 9999;
}
else
return (stackRef[topIndex]);
}
public boolean empty() {return (topIndex == -1);}
}
An example class that uses StackClass follows:
public class TstStack {
public static void main(String[] args) { StackClass myStack = new StackClass(); myStack.push(42);
myStack.push(29);
System.out.println("29 is: " + myStack.top()); myStack.pop();
System.out.println("42 is: " + myStack.top()); myStack.pop();
myStack.pop(); // Produces an error message
}
}
One obvious difference is the lack of a destructor in the Java version, obviated by Java’s implicit garbage collection.5
11.4.4.2 Evaluation
Although different in some primarily cosmetic ways, Java’s support for abstract data types is similar to that of C++. Java clearly provides for what is necessary to design abstract data types.
11.4.5 Abstract Data Types in C#
Recall that C# is based on both C++ and Java and that it also includes some new constructs. Like Java, all C# class instances are heap dynamic. Default constructors, which provide initial values for instance data, are predefined for all classes. These constructors provide typical initial values, such as 0 for int types and false for boolean types. A user can furnish one or more constructors for any
5. In Java, the finalize method serves as a kind of destructor.

498 |
Chapter 11 |
Abstract Data Types and Encapsulation Constructs |
class he or she defines. Such constructors can assign initial values to some or all of the instance data of the class. Any instance variable that is not initialized in a user-defined constructor is assigned a value by the default constructor.
Although C# allows destructors to be defined, because it uses garbage collection for most of its heap objects, destructors are rarely used.
11.4.5.1 Encapsulation
As mentioned in Section 11.4.2, C++ includes both classes and structs, which are nearly identical constructs. The only difference is that the default access modifier for class is private, whereas for structs it is public. C# also has structs, but they are very different from those of C++. In C#, structs are, in a sense, lightweight classes. They can have constructors, properties, methods, and data fields and can implement interfaces but do not support inheritance. One other important difference between structs and classes in C# is that structs are value types, as opposed to reference types. They are allocated on the runtime stack, rather than the heap. If they are passed as parameters, like other value types, by default they are passed by value. All C# value types, including all of its primitive types, are actually structs. Structs can be created by declaring them, like other predefined value types, such as int or float. They can also be created with the new operator, which calls a constructor to initialize them.
Structs are used in C# primarily to implement relatively small simple types that need never be base types for inheritance. They are also used when it is convenient for the objects of the type to be stack as opposed to heap allocated.
11.4.5.2 Information Hiding
C# uses the private and protected access modifiers exactly as they are used in Java.
C# provides properties, which it inherited from Delphi, as a way of implementing getters and setters without requiring explicit method calls by the client. As with Objective-C, properties provide implicit access to specific private instance data. For example, consider the following simple class and client code:
public class Weather {
public int DegreeDays { //** DegreeDays is a property get {
return degreeDays;
}
set {
if(value < 0 || value > 30) Console.WriteLine(
"Value is out of range: {0}", value);
else
degreeDays = value;
}

11.4 Language Examples |
499 |
}
private int degreeDays;
...
}
...
Weather w = new Weather();
int degreeDaysToday, oldDegreeDays;
...
w.DegreeDays = degreeDaysToday;
...
oldDegreeDays = w.DegreeDays;
In the class Weather, the property DegreeDays is defined. This property provides a getter method and a setter method for access to the private data member, degreeDays. In the client code following the class definition, degreeDays is treated as if it were a public-member variable, although access to it is available through the property only. Notice the use of the implicit variable value in the setter method. This is the mechanism by which the new value of the property is referenced.
The stack example is not shown here in C#. The only difference between the Java version in Section 11.4.4.1 and the C# version is the output method calls and the use of bool instead of boolean for the return type of the empty method.
11.4.6Abstract Data Types in Ruby
Ruby provides support for abstract data types through its classes. In terms of capabilities, Ruby classes are similar to those in C++ and Java.
11.4.6.1 Encapsulation
In Ruby, a class is defined in a compound statement opened with the class reserved word and closed with end. The names of instance variables have
aspecial syntactic formthey must begin with at signs (@). Instance methods have the same syntax as functions in Ruby: They begin with the def reserved word and end with end. Class methods are distinguished from instance methods by having the class name appended to the beginning of their names with a period separator. For example, in a class named Stack,
aclass method’s name would begin with Stack. Constructors in Ruby are named initialize. Because the constructor cannot be overloaded, there only can be one per class.
Classes in Ruby are dynamic in the sense that members can be added at any time. This is done by simply including additional class definitions that specify the new members. Moreover, even predefined classes of the language, such as String, can be extended. For example, consider the following class definition:

500 |
Chapter 11 |
Abstract Data Types and Encapsulation Constructs |
class myClass
def meth1
...
end
end
This class could be extended by adding a second method, meth2, with a second class definition:
class myClass
def meth2
...
end
end
Methods can also be removed from a class. This is done by providing another class definition in which the method to be removed is sent to the method remove_method as a parameter. The dynamic classes of Ruby are another example of a language designer trading readability (and as a consequence, reliability) for flexibility. Allowing dynamic changes to classes clearly adds flexibility to the language, while harming readability. To determine the behavior of a class at a particular point in a program, one must find all of its definitions in the program and consider all of them.
11.4.6.2 Information Hiding
Access control for methods in Ruby is dynamic, so access violations are detected only during execution. The default method access is public, but it can also be protected or private. There are two ways to specify the access control, both of which use functions with the same names as the access levels, private and public. One way is to call the appropriate function without parameters. This resets the default access for subsequently defined methods in the class. For example,
class MyClass
def meth1
...
end
...
private
def meth7
...
end
...
end # of class MyClass

11.4 Language Examples |
501 |
The alternative is to call the access control functions with the names of the specific methods as parameters. For example, the following is semantically equivalent to the previous class definition:
class MyClass def meth1
...
end
...
def meth7
...
end
private :meth7, ...
end # of class MyClass
In Ruby, all data members of a class are private, and that cannot be changed. So, data members can be accessed only by the methods of the class, some of which may be accessor methods. In Ruby, instance data that are accessible through accessor methods are called attributes.
For an instance variable named @sum, the getter and setter methods would be as follows:
def sum
@sum
end
def sum=(new_sum)
@sum = new_sum
end
Notice that getters are given the name of the instance variable minus the @. The names of setter methods are the same as those of the corresponding getters, except they have an equal sign (=) attached.
Getters and setters can be implicitly generated by the Ruby system by including calls to attr_reader and attr_writer, respectively, in the class definition. The parameters to these are the symbols of the attribute’s names, as is illustrated in the following:
attr_reader :sum, :total
attr_writer :sum
11.4.6.3 An Example
Following is the stack example written in Ruby:
#Stack.rb - defines and tests a stack of maximum length
#100, implemented in an array

502 |
Chapter 11 |
Abstract Data Types and Encapsulation Constructs |
class StackClass
#Constructor def initialize
@stackRef = Array.new(100) @maxLen = 100
@topIndex = -1 end
#push method
def push(number)
if @topIndex == @maxLen
puts "Error in push - stack is full" else
@topIndex = @topIndex + 1 @stackRef[@topIndex] = number
end end
#pop method def pop
if empty
puts "Error in pop - stack is empty" else
@topIndex = @topIndex - 1 end
end
#top method def top
if empty
puts "Error in top - stack is empty" else
@stackRef[@topIndex] end
end
#empty method def empty
@topIndex == -1
end
end # of Stack class
# Test code for StackClass myStack = StackClass.new myStack.push(42)