In C++, the term "class" refers to three different, but related things:
a language construct, that encapsulates the definitions of data members, member functions, nested types, etc.;
a particular kind of type, defined by a class construct (or by "struct" which is a special case of "class");
a set of types consisting of a type and all of its derivatives, direct and indirect.
In Ada 95, the term "class" refers only to the third of the above definitions. Ada 95 (and Ada 83) has three different terms for the concepts corresponding to the above three things:
- "package" encapsulates the definitions of types, objects, operations, exceptions, etc which are logically related. (The operations of a type defined immediately within the package where the type is declared are called, in Ada 95, the "primitive operations" of the type, and in some sense, define the "primitive" semantics of the type, especially if it is a private type.)
- "type" is characterized by a set of values and a set of primitive operations (there are a million definitions of "type," unfortunately, but you know what I mean...);
- "class" is a set of types with similar values and operations; in particular, a type and and all of its derivatives, direct and indirect, represents a (derivation) class. Also, the set of integer types form the integer "class," and so on for the other language-defined classes of types in the language.
Some OOP languages take an intermediary position. In CLOS, a "class" is not an encapsulating construct (CLOS has "packages"). However, a "class" is both a type and a set of types, depending on context. (Methods "float" freely.)
The distinction Ada 95 makes between types and classes (= set of types) carries over into the semantic model, and allows some interesting capabilities not present in C++. In particular, in Ada 95 one can declare a "class-wide" object initialized by copy from a "class-wide" formal parameter, with the new object carrying over the underlying type of the actual parameter. For example:
procedure Print_In_Bold (X : T'Class) is
-- Copy X, make it bold face, and then print it.
Copy_Of_X : T'Class := X;
begin
Make_Bold (Copy_Of_X);
Print (Copy_Of_X);
end P;
In C++, when you declare an object, you must specify the "exact" class of the object -- it cannot be determined by the underlying class of the initializing value. Implementing the above procedure in a general way in C++ would be slightly more tedious.
Similarly, in Ada 95 one can define an access type that designates only one specific type, or alternatively, one can define one that can designate objects of any type in a class (a "class-wide" access type). For example:
type Fancy_Window_Ptr is access Fancy_Window;
-- Only points at Fancy Windows -- no derivatives allowed
type Any_Window_Ptr is access Window'Class;
-- Points at Windows, and any derivatives thereof.
In C++, all pointers/references are "class-wide" in this sense; you can't restrict them to point at only one "specific" type.
In other words, C++ makes the distinction between "specific" and "class-wide" based on pointer/reference versus object/value, whereas in Ada 95, this distinction is explicit, and corresponds to the distinction between "type" (one specific type) and "class" (set of types).
The Ada 95 approach we believe (hope ;-) gives somewhat better control over static versus dynamic binding, and is less error prone since it is type-based, rather than being based on reference vs. value.
In any case, in Ada 95, C++, and CLOS it makes sense to talk about "class libraries," since a given library will generally consist of a set of interrelated types. In Ada 95 and CLOS, one could alternatively talk about a set of "reusable packages" and mean essentially the same thing.
(Tucker Taft) |