What is "object-oriented programming" (1991 revision)

zhaozj2021-02-16  53

What is IS "Object-Oriented Programming" (1991 Revised Version) Bjarne Stoustrup AT & T Bell Laboratories Murray Hill, New Jersey 07974 Translator Do not simply deepen this paper as an introduction to C features. Its significance is that on the one hand, the evolution of the programming style is introduced, and the motivation behind this evolution. In another aspect, it specifically clarifies that object-based (OB) and object-oriented (OO) is very meaningful. We can see that whether it is OB or OO, it is just an organizational form of a program. This pointed out what kind of problem with OO focused on solution (how to organize how to organize elasticity, easy to reuse and understand), and do not solve any problem (design, algorithm design), etc. Abstract "Object-Oriented Programming" and "Data Abstraction" have become commonly used programming terms, however, few people can achieve consistent understanding of their meaning; this article is based on the language as a language such as ADA, C , Module 2, Simula and Smalltalk. This gives an informal definition. Basic ideas are the ability to "support data abstraction" to define and use new data types, and the "supporting object-oriented programming" is equivalent to the expression capability of the class level. At the same time, the generic programming language must be discussed to support such programming styles. Although C is used in the text, the scope of its discussion is not limited to this language. 1 Introduction is not all languages. It is generally believed that APL, ADA, CLU, C , Loops and Smalltalk are object-oriented, I have also heard about discussions on object-oriented design using C, Pascal, Module-2, and Chill. So can I try to use Fortran and COBOL to perform object-oriented design? I think that it must be feasible. In many circles, "object-oriented" has become a "excellent" high-tech synonym, which can see the following three paragraphs in the business publishing area: Ada is an excellent object-oriented is excellent, so Ada is an object-oriented this article from General Programming The perspective of the language states "Object-oriented" is awarded: Section 2 compares the difference between data abstraction and object-oriented, and distinguishes them with other programming styles; at the same time, it is pointed out to support different programming. An important mechanism required for style. Section 3 states the language mechanism required to efficiently support data abstraction. Section 4 discusses the facilities needed to support object-oriented. Section 5 states that traditional hardware architecture and operating systems are limited to data abstraction and object-oriented programming. The examples of the text use C to write, this part is for the purpose of introducing C , part is because C is a small number of languages ​​that support data abstraction, object-oriented programming and traditional programming style. This article does not discuss concurrency and special hardware support involved in support specific high-level language characteristics. 2. Programming Paradigms Object-Oriented Programming is a programming technology for writing quality codes for a class of issues. A language is called "object-oriented" if it supports (support) object-oriented programming. There is an important difference here. A language is called "Support" programming technology, if it provides means for easy implementation (convenient, securely, and efficient) this style programming; contrary, if additional skills and means need to be used Get a certain style of encoding, this language is not "support" of the programming style, we can only say this language "Enable" has a programming style.

For example, people can write structured programs using Fortran, write type security programs in the C language, using data abstraction technology in Module-2, but these tasks have unnecessary difficulties, because these languages ​​are not Support "those programming style. Supports for some programming style not only means the language provides a clear language and can be used directly, but also means a certain check in the compilation time and run time to prevent the code from unintentionally deviating from the style. Type Check is a particularly obvious example, an unisforced inspection and runtime inspection can also expand the language to support specific programming styles. At the same time, this support can be enhanced like standard libraries and programming environments. It is not necessarily that if a language has been supported, it must be better than other languages ​​that do not support this feature. There is too much antique here. Important is not a language that has a language, but it has the characteristics that it is enough to support specific programming winds within a particular area. 1. All features must be clear and elegantly integrated into the language. 2. Use these features by combining these features must be sufficient to obtain solutions without having to use other features. 3. The characteristics of counterfeiting and "special purpose" must be as small as possible. 4. All features cannot strong with too much overhead in those programs that do not use them. 5. Users only need to know that language subsets that are configured in the programs can be written. The last two points can be summarized as "programmers will not be hurt by them." This feature is preferably abandoned if there is any question for a feature. Plus a property in the language is much easier than a range of features or from its literature. The following will list some programming styles and their core language mechanisms, but do not intend to discuss it is too in-depth and cumbersome. 2.1 Process programming initial (may also be the most commonly used in current) programming style: decided to need the best algorithm design that can be obtained in those processs is to process the process and execution of algorithms, language provides this parameter delivery Give functions and mechanisms that return values ​​from the function. The literature related to this way of thinking discusses the different ways of passage, distinguishes different parameters, and various processes (processes, functions, macros), and more. Fortran is the earliest process language, Algol60, Algol68, C and Pascal are some subsequent process languages. The square root function is a typical example, which simply generates the square root of the incoming parameter. To this end, the function performs a simple mathematical operation: Double SQRT (Double Arg) {// the code for calculting a Square root} void Some_Function () {double root2 = SQRT (2);} From the perspective of the program structure , The function has made a clear relationship between the algorithm. 2.2 Data Hide Over time, the focus of programming is focused from the process design to the organization of the data, which reflects the growth of the program size. A set function of data and direct operation data is called a module. The style of programming is: decided to need those module breakdown programs, making data hidden in different modules in this style called "Data Hide Rules". In cases where do not have to bind to data and related to it, you can use only process program design. In particular, those techniques used to design a "good process" can now be applied to each process within the module.

The most common example is to define a stack module. There is a problem when designing: 1. Provide a user interface for the stack module (for example, function push () and pop ()) 2. Ensure the representation of the stack (for example, an element Array) can only access the interface through the module 3. Make sure the stack is performed before it is first accessed, the following is an unhappy stack module external interface: // Declaration of the interface of module stack of charater Char pop (); void push (char); const stack_size = 100; assuming this external definition is saved in the stack.h file, and the inside of its module is as follows: #include "stack.h" static char v [stack_size]; Static char * p = v; char pop () {// check for underflow and pop} void push (char c) {// check for overflow and push} It is very convenient to modify the stack to the chain list, users cannot Access the internal representation of the stack (because V and P have been declared as static, so you can only reference them within the module that declares their modules). You can use this stack module like this: #include "stack.h" void Some_Function () {char c = pop (push ('c')); if (c! = 'C') error ("impossible");} Pascal does not provide a satisfactory facility to implement this binding. The only way to isolate one of the other parts of a name and program is to make it within a process, which results in the strange process nested and excessive dependence on the global data. The expression of the C language is slightly better. In the example described above, the data can be saved in the same file in the examples described above to form a module, whereby the programmer can control which name is global visible ( The name being declared for Static is only visible in this module). As a result, C language can support modularization to some extent; however C lacks a general framework for using this mechanism, while the Name Access is too low at STATIC. Pascal's subsequent language, Module-2, more far. It forms the concept of modules, providing some basic languages, such as the material definition module declaration, the explicit control of the name range (Import, Export), the initialization mechanism of the module, and the use of the use of these mechanisms the way. C and Module-2 The difference between this field can be summarized, c just "enabled" to decompose the program into modules, and Module-2 "support" this technology. 2.3 Data Abstraction Modular Programming Development Become a programming style of a type of data set in a type of management module.

If someone needs two stacks, he may design a stack management module with the following interface: class stack_id; // stack_id is a type // no Details About stacks or stack_ids area knower stack_id create_stack (int size); // Make A Stack and Return ITETIFIER DESTROY_STACK (Stack_ID); Void Push (stack_id) CHAR POP (Stack_ID) is of course a major improvement with respect to those in the past. However, "Types" implemented in this manner is also distinguished from the language of the built-in type of language. Each type management module must define its own mechanism to generate its own "variable"; there is no clear method here to give variables to identifically, and it is impossible to let the compiler and programming environment understand the name of the variable. At the same time, there is no way to let these variables obey the common variable scope rules and parameters transfer rules. The type established by the module mechanism is different from the built-in type in many important aspects. At the same time, its support is much lower than that of the built-in type. For example: void f () {stack_id s1; stack_id s2; s1 = create_stack (200); // OOPS: Forgot To Create S2 Shar C1 = POP (S1, PUSH (S1, 'A')); if (C1! = 'c') Error ("Impossible"); char C2 = POP (S2, PUSH (S2, 'A')) IF (C2! = 'c') Error ("Impossible"); Destroy (S2); // OOPS, Forgot To Destroy S1} In other words, the module concept that supports data hidden style is just enabled, but it does not support this style. The language such as ADA, CLU and C is solved by allowing user definitions and "types" similar to built-in behavior. This "type" is often referred to as "abstract data type". As a result, the programming style becomes: decided to need only data hidden techniques to implement a set of complete operations for each type to implement multiple objects that do not need to generate multiple objects for a type. Moderate types such as rational numbers and plurals are common examples of abstract data types: class complex {Doube Re, implement: complex (double r, double i) {re = r; im = i;} complex (double r) {RE = R; IM = 0;} // float-> Complex Conversion Friend Complex Operator (Complend Compelx Operator); Friend Compelx Operator- (Complex, Complex); // Binary Minus Firend Complexy Opeator- (Complex); // Unary Minus Friend Compelx Operator * (Complex, Complex); Friend Complex Operator / (Complex, Complex); // ...} The declaration of class complex (user-defined type) determines a plural "representation" and a group of it-related operating. "Representation" is private, that is, it can only access RE and IM through functions declared in the Complex class.

The function can be defined as follows: Complex Operator (Complex A1, Complex A2) {Return Complex (A1.RE A2.RE, A1.IM A2.IM);} can be used like this: Complex A = 2.3; Complex B = 1 / a; complex c = ab * complex (1, 2.3); // ... c = - (A / b) 2; most (but not all) modules can use "type" to get better expression . For those concepts more suitable for expressing a "module", the programmer can define a type of only a single object to be replaced. Of course, the language can also provide an independent module mechanism outside of the custom type mechanism. 2.4 Data Abstraction Problem A Abstract Data Type Defines a class of black boxes, once the definition is completed, then other parts of the program no longer interact. Unless it is modified, it is difficult to use it for new purposes. Consider defining a type shape for a graphical system. Assume that the current system supports circles, triangles, and squares, and there are other related classes: class point {/*...*/}; class color {/*...*/}; Shape class may be defined as this: ENUM KIND {Circle, Triangle, Squre}; Class Shape {Point Center; Color Color COL; Kind K; // REPRESENTATION OF SHAPE PUBLIC: POINT WHERE () {Return Center;} Void Move (Point To) {center = TO; DRAW (); Void draw (); void rotate (int); // more operation}; in order to allow Draw, Rotate knows what shape is currently processed, where the type of domain "k" must exist (in class Pascal language You can use a variable record with tag K), the function DRAW can be defined as this: void Shape :: Draw () {switch (k) {copy circle: // Draw A Circle; Break; Case Triangle: // DRAW A Triangle; Break; Case Square: // Draw A Square; Break;}} This is confusing. A function like DRAW must understand the current "shapes" currently existing, so each of these functions must be rewritten whenever the system adds a new "shape". In order to define a new "shape", it must be checked and all operations of the Shape may also be modified. Therefore, unless the source code can be repaired, it will not be possible to add new "shapes" in the system. Since adding a new "shape" will result in all important operations of Shape, this means that programming requires higher skills and can also introduce bugs for existing "shapes". At the same time, the application framework (or part of the general type shape) may require each particular "shape" must have a staggered representation, which will cause a large limit for specific shapes. 2.5 Object-Oriented Programming Problems Differentially distinguishes that there is no general property (color, painting) and proprietary attributes of a particular shape (circle having a radius, and drawing a plot using a circle function). This distinction is expressed and utilized to form an object-oriented programming. Only languages ​​that can be used directly to direct this distinction is to support object-oriented, other languages ​​are not. The SIMULA inheritance mechanism provides a solution.

First, specify a class to define the general properties of the shape: Class Shape {Point Center; Color Col; PUBLIC: Point where () {Return Center;} Void Move (Point To) {center = To; Draw ()} Virtual Void Draw (); Virtual Void Rotate (int); // .......} Call interface can be determined, but the function that is not yet determined is marked as "Virtual" (in Simula and C , it can be A subclass is redefined). After these definitions, we can write a general function of the operation: void rotate_all (interface * v, int size, int /) // rotate all memory of vector "v" of size "size" "Angle" degrees {For (int i = 0; i

For example: Class Vector {Int Sz; INT * V; PUBLIC: Void Init (INT Size); // Call Init To Initialize Sz and V BEFORE THE FIRST Use of A // Vector // ...} Vector v; // Don't Use v here v .init (10); // Use v here This is easy to cause errors and not elegant enough. A better solution allows type designers to provide a special function for initialization; given this function, allocation, and initializing a variable becomes the same operation. This particular function is often referred to as constructor. In some cases, an object may not be very simple, so it often requires a peer-to-peer operation to perform clearance after the object is last used. In C , such a clear function is called a destructor. Consider a vector type: class vector {int SZ; int * v; public: vector (int); // constructor ~ vector (); // deStructor int & operator []/);}; vector constructor can be defined To allocate space, like this: Vector :: vector (int S) {if ("Bad Vector Size '); SZ = S; V = New Int [S]; // Allocate An Array of "s" integers} vector destructor's destructor :: ~ vector :: ~ vector () {(注: This is best delete v; // deallocate the memory pointed to by v} C Wasp collection is not supported, which allows a type of technique to manage storage space without requiring users to intervene. Storage management is the operation of the construction / destructuring function, but they are often used to perform. Things. 3.2 Assignment and Initialization For many types, controlling its initialization and clearing procedures is enough, but not all types are true. Sometimes it is necessary to control the copy process, consider the vector: Vector V1 [100] ; // make a new vector v2 initialized to v1 v1 = v2; // Assign V2 To V1 must have mechanisms here to define V2 initialization and the meaning of V1 assignment, of course, can also choose to provide a mechanism to ban This copy. The ideal case is that these two mechanisms exist. For example: class vector {int * v; int SZ; public: // .... void operator = (vector &); // Assignment Vector (Vector & Vector &) ; // initialization}; gives the user-defined operation to explain the value of the Vector and initialization.

Assignment can be defined like this: (Translation: Since Operator = (Vector & A) in the above class vector is declared as a void type, here is the best for Void Vector :: Operator (vector & a)) Vector :: operator = (Vector & A) // Check size and copy elements {if (sz! = a.sz) Error ("Bad Vector Size for ="); for (int = 0; I}

Although the assignment operation can rely on an "old" VECTOR object, the initialization operation must be different, for example:

Vector :: Vector (vector & a) // Initialize a Vector from another vector

{

SZ = a.sz;

v = new int tent [sz];

For (int i = 0; i

}

In C , a constructor such as X (X &) defines the initialization process of another object that constructs X from an object from X. In addition to clearly

Outside the X (X "is also used to process the return value of the passage of the pass value.

In C , the assignment operation of the object can be prohibited by the assignment declaration as a private.

Class x {

Void Operator = (x &); // Only MEMBERS OF X CAN

X (x &); // copy an x

// ...

PUBLIC:

// ...

}

The ADA does not support construction, destructuring, pairing overload and user-defined parameters transfer and return mechanism, which seriously limits the type of user-defined type,

At the same time, forced programmers to return to "data hide" technology, that is, users must design and use type management modules instead of real types.

3.3 Parameterization Type

Why do we want to define an integer type of Vector? You know, users often need a type of unknown to the author of Vector

Vector. Therefore, VECTOR should define a expression that can be referenced as a parameter:

Class vector

{// Vector of Elements of Type T

T * v;

Int Sz;

PUBLIC:

VECTOR (Int S)

{

IF (S <= 0) Error ("Bad Vector Size");

v = new t [sz = s]; // allocate an array of "s" "t" s

}

T & OPEARTOR [] (INT I);

INT size () {return sz;}

// ...

}

Specific type of Vector can be defined and used in this way:

Vector

V1 (100); // v1 is a vector of 100 integers

Vector

V2 (200); // v2 is a Vector of 200 Complex Numbers

V2 [i] = complex (V1 [x], v1 [y]);

ADA, CLU and ML support parameterization type. Unfortunately, C does not support (translation, the current C standard supports parameterization type, called template); here

The marker used is just to demonstrate; but when necessary, macro can be used to simulate parameterization types. And those who specify all types of classes do not have more overhead to be introduced at runtime.

In general, a parameterized type will always depend on certain aspects of parameter types. For example, some operations of the Vector assume that the parameter type defines assignment

Work. So how do people guarantee this? One solution is designer that requires parameterization types to show this dependency. For example, "T must be a fixed

The type of assignment operation. "Another way to make the specifications of the parameterized type and parameter types of parameter types are independent, and the compiler can detect

There is no operation of the operation, and the corresponding error prompt can be given. E.g:

Cannot Define Vector :: Operator [] (Non_copy &):

TYPE NON_COPY DOES NOT HAVE OPERATOR =

This technology allows us to process dependence between parameter types and parameterization types at the "Operation" level. For example, we may define one

VECTOR with a sorting function, the sorting operation may be <, <=, and = operation of the parameter type. However, as long as we do not call the sequenile function, we

Also can be used to parameterize the type without the type of operation.

Between each type generated from the parameterized type is independent of each other, this is a problem. For example, Vector

And Vector

It

It is completely unrelated. Ideally, people can express and utilize commonality between the various types generated from the same parameterized type.

Such as Vector

And Vector

Both have one and type-independent SIZE () operations. Promoting SIZE from the definition of Vector

The instance type sharing is possible, but its process is not simple. Interpretation type language or simultaneous support of parameterization types and inheritance mechanisms in this aspect

Advantages.

3.4 abnormal handling

As the scale grows, especially when the library is released, it provides a standard for processing errors (or more generally, "abnormal conditions")

Mechanism is important. The ADA, Algol68, and CLU each support a standard mechanism for processing anomalies. Unfortunately, C does not directly support exception handling (translation,

The current C standard has supported exception handling), but must use the function pointer, "exception object", "error status", and C library function Signal and

Longjump and other mechanisms to fake. These mechanisms are not normal, and it is also possible to provide a standard framework for processing errors.

Recise the example of the vector. What happens when an off-border index value is passed to an Subscribe operation? Set of Vector

The gauge should be able to specify a default behavior for this:

Class vector {

...

Except vector_range {

// define an exception called vector_range

// and specify default code for handling it

Error ("Global, Vector Range Error");

EXIT (99);

}

}

Vector :: OPEARTOR [] () can trigger an exception handling code instead of calling the error function:

INT & Vector :: Operator [] (INT I)

{

IF (0

Return V [I];

}

This causes the stack to return until a handle that can handle the vector_range exception. This exception handler is then performed.

You can define an abnormal handle for a specific code block: Void f () {

Vector v (10);

Try {// Errors Here Are Handled by The Local

// Exception Handler Defined Below

// ...

INT i = g (); // g Might Cause A Range Error Using Some Vector

v [I] = 7;

}

Except {

Vector :: Vector_Ranger:

Error ("F () Vector Ranger Error");

Return;

}

// Error Here Are Handled by The Global

// Exception Hander Defined in Vector

INT i = g ();

v [i] = 7;: // might cause a Range Error Using Some Vector

// Potential Range Error

}

There are many ways to define an exception and an exception handle. The abnormal mechanism profile listed here is from CLU and Module-2 .

of. This type of abnormality can be implemented as until the exception is thrown. Can also be easily used with setJMP and

Longjup simulates it.

So, is it completely faked in the C in Semantics in C ? Unfortunate, can't. The problem is that when an abnormality occurs

When the running stack must be rolled back to the location of the installation exception handle, in C , this involves the destruction letter called the object being destroyed during the backup process.

number. The longjmp function using C does not do this; in general, the user can not do this.

3.5 forced

It has been proven that user-defined mandatory is very useful technology, for example, constructor complex (double) implies a from double to Complex

Mandatory. Programmers can clearly point out forced or when necessary, if there is no second-meaning, the compiler can also in secretly introduce it:

Complex a = complex (1);

Complex b = 1; // implicit: 1-> Complex (1)

A = B Complex (2);

A = B 2 // Implicit: 2-> Complex (2)

C introduction to user-defined forces is that the arithmetic expression of mixed mode in the language that supports arithmetic operations is common; at the same time, participate in operations

User-defined types (eg, matrices, strings, machine addresses, etc.) can also be mapped with each other naturally.

From the perspective of program organization, there is a type of mandatory to prove is exceptionally effective:

Complex a = 2;

Complex b = a 2; // Interpereted As Operator (A, Complex)

B = 2 a; // Interpereted As Operator (COMPLEX (2), A)

Only one function is required when explaining the ' ' operation, and for the type system, the two operands are similar to it. Further, we see,

You can smoothly integrate these two concepts smoothly without any adjustment of the integer concept. with

"Pure object-oriented system" is very different, where these operations will be explained below:

A 2;: //a.opeartor (2) 2 a ;: //2.operator (a)

This must modify classes Integer to make the 2.Operator (a) legalize. When adding a new function in a system, modify the existing code is a must

Try to avoid, in general, object-oriented programming technology can support this goal well, but here, data abstraction technology provides better solutions.

Decision.

3.6 Iterator (Iterators)

It is generally believed that the language that supports data abstraction must provide a means of defining control structures. In particular, there is often a need for a user to loop to access one container.

The mechanism of elements contained in the type, and it is not possible to force the user to rely on the implementation details of the container type. If there is a powerful mechanism for defining types,

It is also possible to overload the operator, and this goal can be achieved without incorporating the mechanism of the independent definition control structure.

For vector, users can determine their order by subscript, so it is not necessary to define iterators. However, I still define a demonstration of this technology.

Surgery. Iteractors can have a lot of style, I prefer to pass overload function operators:

Class vector_iterator {

Vector & v;

INT I;

PUBLIC:

Vector_iterator (vector & r) {i = 0; v = r;}

Int operator () () {Return i

}

Now we can declare and use iterators like this:

Vector V (SZ);

Vector_iterator next (v);

INT I;

While (i = next ()) Print (i);

A object can activate multiple iterator objects at the same time; at the same time, a type can define a variety of different types of iterators for different

Loop operation. The iterator is a fairly simple control structure, or it can define a more general control mechanism, such as the C standard library provides CO-

Routine class [15].

For many container types, such as vectors, iterative mechanisms can be defined as part of the type itself to avoid introducing a separate iterator. can

Vector is defined as a "current state":

Class vector {

INT * V;

Int Sz;

INT CURRENT;

PUBLIC:

// ...

INT next () {return (Current )

INT prev () {RETURN (0 <--current)? v [current]: 0;

}

So you can operate like this:

Vector V (SZ);

INT I;

While (i = v.next ()) Print (i);

Compared with the iterator, such a solution is not general; but in an important special case it reduces overhead: may we only need a type of

Iterator and only have an iterator object at the same time. If necessary, you can add more general machines above this simple solution.

system. Please note that using this simple solution is more designed than using an iterator. Iterator technology can also be designed as the same one

The generation type can be bound to a different container type, so that different container types can be accessed through an iterator.

3.7 Implementation

Support for data abstraction is mostly defined as language features and is implemented by compilers. But the parameterization type is best able to pass through a semantic language.

Multi-understanding connector is supported; at the same time, abnormal processing requires support for operating environment. They can get very much without sacrificing generality and ease of use.

Good compilation speed and efficiency.

As defined types of ability, programs begin more dependent from some libraries (not limited to those described in language manual)

Rong). This naturally requires tools to express which parts in the program are inserted into the library, and which parts are extracted from the library; also need tools to find out which parts of the library contain which parts of the library are actually programs Use the like.

For compilation languages, it is important to make the code to minimize compilation work after modification. At the same time, the connector / loader can

It is also very critical to load the capacity of a large amount of independent and useless code without loading the execution code. Special point to point out, if a type is only a few

Several operations are called, while the library / connector / loader loads all the operations of this type into memory. It is particularly poor.

4. Support for objects

There are two mechanisms that support the basic role in supporting object-oriented programming, the first is the inheritance mechanism of the class; the second is, when compiling, one is not determined

When an actual type of an object, the specific method of the call should be determined based on the actual type of object at runtime. Among them, the setting of method call mechanism

The count is the key. At the same time, the support technology of data abstraction as described above is equally important for supporting object-oriented, because data abstraction,

And efforts to be elegantly supported in the language are also effective in object-oriented technology. The success of these two technologies depends on the type of type.

And can use these types efficiently, easy and flexibly. Object-oriented technology can design more, more spiritual relative to data abstraction

Live data type.

4.1 call mechanism

Support for object-oriented key language features a method for calling it for a given object. For example, give a pointer P, how to process call p-> f

(arg)? There is a series of options here.

In the language of C and Simula, a language that is widely applied in a static type check can be made by means of a type system to make a selection between different invoking methods. in

In C , there are two ways to call:

[1] Ordinary method call: Specific call that method can be decided in compilation time (by looking for the compiler's symbolic table), at the same time

The process call mechanism adds a pointer to the identity of the object. If the standard process call is not high enough in some occasions, the function can be

Declare be inline, the compiler is trying to exhibit a function in the call location. In this way, people can get a similar macro

The mechanism does not sacrifice the semantics of the standard function. Such optimization measures are also valuable for data abstraction.

[2] Virtual function call: Function call relies on the actual type of object, in general, the actual type of object can only be determined at runtime. typical

The case is that the pointer P has the type of a base class B, and the P-pointed object is a subclass D of B (I wants the Shape and Circle in the above example).

The call mechanism must check the compiler to determine the function to call the function. Once the function is found, if you say D::, you can use it.

The way described above is called. In the compile time, the name f conversion becomes a index of a function pointer table. Such a call mechanism is almost and common methods

The call is as effective, in standard C , only more 5 memory references are required.

Weak type language needs to be more complex mechanisms. In languages ​​like SmallTalk, you must save the names of all methods of classes for use in runtime.

Execution lookup:

[3] Method Trigger: First find the correct method name of the name through the P pointing, and then find if there is a method "f", such as

The existence is called; otherwise an error. And static type languages ​​are different in compilation time execution function names, where the method name is found to find a actual

Object is performed.

Compared with the virtual function call, the method is triggered, but it is more flexible. Since the static parameter type check is usually unable to perform, this call mode must be used with dynamic type checks.

4.2 Type Check

The "shape" described above shows the power of the virtual function. So what is the way to trigger? The answer is, we can now

Try calling any way in any object!

The ability to trigger an arbitrary method allows the designer of the class library to pass the responsibility of the correct processing data type to the user's head, of course, this

Simplified the design of the library. E.g:

Class Stack {: // Assume Class Any Has A Member Next

Any * v;

Void Push (any * p)

{

P-> next = V;

v = P;

}

Any * pop ()

{

IF (v == 0) Return Error_Obj;

Any * r = v;

v = V-> next;

Return R;

}

}

In this way, users must have a responsibility to ensure that the following types of matching errors are avoided:

Stack

CS;

CS.PUSH (New Saab900);

CS.PUSH (New Saab37B);

Plane * P = (Plane *) cs.pop ();

P-> Takeoff ();

P = (plane *) cs.pop ();

P-> TACKOFF (); // OOPS! Run Time Error: a saab 900 is a car

// a car does not have a takeoff method;

The message processing mechanism can detect that the program tries to treat a car as a plane, so triggered an error. However, only when users are programmers

This error message may have some comfort; due to lack of static type check, it is difficult to ensure that it is not included in the final released program.

Contains such errors. Naturally, such contradictions in a method-based language will be more prominent in a method-based language.

Combining parameterization types and virtual functions can be used in the flexibility, simplicity, and convenience of library design, and the convenience of library.

At the same time, you don't need to give up static types or introduce overhead in space and time, for example:

Stack

CS;

cs.push (new saab900); // compile time error:

// Type Mismatch: Car * Passed, Plane * EXPECTED

CS.PUSH (New Saab37B);

Plane * p = cs.pop ();

P-> Takeoff (); // Fine: a saab 37b is a place.

P = cs.pop ();

P-> Takeoff;

Some differences between program style and dynamic type check / method triggering based on static type check / virtual function calls. For example,

A class of SIMULA and C specifies a determined interface for the objects of all its subclasses, and the classes in SmallTalk are specified for the objects of all their subclasses.

An initial interface. In other words, the class in the SmallTalk is a minimum interface specification, and the user can attempt to attempt all other in the interface.

The method of the C class is a determined interface specification, and the user can be confident that only the methods that are defined in the interface can be compiled.

4.3 inheritance

Consider a support method to find but do not support inheritance, can this language be called support for object-oriented? I don't think it is. Obviously, we can

Using method finding techniques to make objects to adapt to specific application environments, and therefore can complete a lot of interesting things. However, in order to avoid confusion, we still need a system to combine the method of the object and the data structure represented as an object. At the same time, in order to make the object's users can

Solve the behavior of an object, but must have a system to represent the commonality between the objects of the object. "This system is standard means"

It is inheritance.

Consider a language that supports inheritance but does not support virtual functions or methods, can this language be called an object-oriented? I don't think:

The "shape" described above does not have a good solution in this language. However, such a language relative to only support data abstraction

Significant a lot. This view comes from such observations: many Simula and C programs use inheritance to organize their structure, but there is no

There is a virtual function. The ability to express the commonality is a particularly powerful tool, for example, without using our joint, we can solve a variety of "shape" needs one

The problem of public expression. However, due to the lack of virtual functions, people must express the specific type of object with a "type field", which leads to the code

The organization lacks module.

Class inheritance is a particularly effective programming tool that can not only support object-oriented programming, but also more widely. Face-oriented

In the programming practice of the like, we can use the base class to express the general concept, and use a subclass to express various special cases. This way is only partially displayed.

The powerful function of inherits, however all functions are virtual functions (or all method-looking) languages ​​emphasize such views. If

Correctly control the contents of inherited from the base class, then the class inherit can be a powerful tool that produces a new type from an existing type. Subcurity and base class

The relationship is not always summarized as special cases and general, "Decomposition" (Factoring) can more accurately express the relationship between the subclass and the base class.

Inheritance is another reasonable programming tool we can't say, it is a reasonable programming tool, and today (even if Simula is born more than 20

Year) It is necessary to say that it is said that it is not too early to say.

4.4 more inheritance

Suppose A is the base class of B, then inherits all attributes of A; that is, in addition to some attributes, B is an A. In this explanation

Under, it is obvious that B will inherit the two base classes A1 and A2 probably useful. This is called more inheritance.

Lift a classic example. Assume that the class library provides two class DISPLAYED and TASK, which are used to represent the display management object and scheduling management object.

Sequence members can generate such a class:

Class my_displayed_task: public displayed, public task {

// my stuff

}

Class my_task: public task {// NOT DISPLAYED

// my stuff;

}

Class my_displayed: public displayed {// not a task

// my stuff;

}

If you only use single inheritance, then programmers can only use two in these three options. This or will result in repetition of the code, or will result in missing elasticity

- Or often both. In C , such an example can use more inheritance and solve, and the inheritance is not distinguished significantly (time and empty

There is no sacrificing static type check.

Embodiments can be resolved in compilation:

Class a {public: f (); ...}

Class b {public: f (); ...}

Class C: Public a, public, b {...};

Void g () {c * p;

P-> f (); // error: ambiguous

}

At this point, C is different from one of the LISP supports multi-oriented dialects. LISP solves the name conflict by relying on the order of declarations, or

The method of the same name from different base classes is considered is equivalent, or combined with the same name method in the base class into a more complex one in a top class.

method.

In C , we can solve the conflict of the name through an additional function:

Class C: Public A, Public B {

PUBLIC:

f ()

{

// C's Own stuff

A :: f ();

B :: f ();

}

....

}

In addition to the concept of Independent Multiple Inheritance, it seems to be more general machines.

Conduct the dependence between a multiple inheritance lattice. In C , you can use a virtual base class to table

One child object in a class object is shared by all other child objects:

Class w {...}

Class bwindow // WINDOW with BORDER

: Public Virtual W

{...}

Class MWindow

: Public Virtual W

{...}

Class BMW // WINDOW with BORDER AND MENU

: Public BWindow, Public MWindow

{...};

This way, in a BMW object, only one W sub object is shared by BWindow and MWindow sub-objects. LISP dialect provides the concept of method combination

Small in programming use so complex class level, but C is not.

4.5 package

Some members who consider class (whether data members or function members) need to be protected to prevent unauthorized access. So how to reasonably define

Which functions can access these members? For object-oriented programming languages, the most obvious answer is "all member letters defined on the object.

Number. But there can be a less apparent inference, that is, we can't fully identify a collection, including all access to these

Protected members' functions; because there is always a new class from these classes that have protection members, and new member functions are defined on the derived class. This

On the one hand, on the one hand, it prevents unexpectedly access to protection members (because we will not "unexpected" to give a new class, in another

Aspects provide elasticity to establish applications using class hierarchy (because we can give yourself access to protect members' capabilities by deriving a new class).

Unfortunately, for a language-oriented language, its answer is different: "You must all have the right access protection in the statement of the statement.

The function of the member, but there is no special requirement for these functions itself, in particular, these functions do not have to be a member function of the class. In C , there is access to access

The non-member function of private members is called a friend function. The class complex defined above has a friend function. Sometimes, declare a function as multiple

The friend function of the class is also useful. If you want to understand a type of meaning, especially when you want to modify it, there is a member function and a friend function.

A complete list is very beneficial.

The following example demonstrates several options in C :

Class b {

// Class MEMBER Are Default Private

INT I;

Void F1 ();

protected:

INT I2;

Void F2 ();

PUBLIC:

INT I3;

Void F3 ();

Friend void g (b *); // any function can be thinkterated as a friends}

Private and protection members cannot be accessed directly by the outside world:

Void h (b * p)

{

P-> F1 (); // error: b :: f1 is private

P-> F2 (); // error: b :: f2 is protected

P-> F3 (); // Fine: b :: f1 is public

}

Protecting members can access in derived classes, but private members cannot:

Class D: public b {

PUBLIC:

Void g ()

{

F1 (); // erro: B: F1 is Private

F2 (); // Fine: B: F2 IS protected, but d is deived from b

F3 (); // Fine: B: F3 is public

}

}

The friend function can access private and protection members like member functions:

Void g (b * p)

{

P-> F1 (); // Fine: B :: F1 is a friend of b () IS A Friend Of B

P-> F2 (); // Fine: B :: F2 IS protected, but g () is a friend of b

P-> F3 (); // Fine: b :: f1 is public

}

With the size of the program, the number of users is increased, and if the user is relatively dispersed in geographical distribution, the importance of members protection mechanism will greatly increase.

Document SNYDER [17] and Stroustrup [18] further discussed the protection problem.

4.6 implementation

In order to support object-oriented programming, it is mainly necessary to improve the runtime system and programming environment. To a certain extent, this is because of the object-oriented language machine

The system has been introduced by data abstraction, so there is no longer a lot of additional features.

Object-oriented technology is further blocked between programming languages ​​and its environment, because various general or specific user-defined types are increasingly charged

Survey in the program. This requires further development of runtime systems, library tools, debuggers, performance measuring tools, and monitoring tools. Ideal is these

Tools are integrated into an environment. SmallTalk is a good example of this.

5 restriction

In order to define a language that can make full use of data hidden, data abstraction and object-oriented technology, our main problem is that any general purpose

The programming must be able to:

1. Run on the traditional computer

2. Corrupted with the traditional operating system

3. Comparable to the traditional program language in time efficiency

4. Used in the main application

This means that this language must be able to efficiently perform arithmetic operations (in terms of floating point calculations); however, it is necessary to access memory

You must be able to use the equipment driver; it must be able to follow the quirky standards developed by the traditional operating system to generate calls. Further, traditional languages ​​must be able

Enough to use a function-oriented language writing, and object-oriented languages ​​must also be able to call functions written in traditional languages.

In addition, if an object-oriented program language relies on technologies that cannot be effectively implemented in the traditional system structure, it is impossible to be a universal language.

Say. Unless special support is obtained, the general implementation of the method trigger mechanism will become a burden.

Similarly, garbage collection may become performance and graft bottlenecks. Most object-oriented languages ​​use garbage collection mechanism to simplify programmers

Do also reduce the complexity of the language itself and the compiler. However, even if we can use garbage collection in some non-critical areas, once you need

To be, we should be able to keep control of the memory. On the other hand, a program language chooses to give up garbage collection, and turn itself.

Storage provides convenient expression means also practical. C is an example. Abnormal handling and concurrency feature are another place in which lurking problems. Any mechanism that relies on the support to support talents can be effectively implemented. Another one with a "low" feature in a language

The method is to use a separate "low" language in the main application field.

6 Conclusion

The inheritance programming is called an object-oriented programming method, based on user-defined types of programming is based on data abstract programming methods. Except

Outside of some exceptions, object-oriented programming can be considered a supercoming of data abstraction. Only the correct support can be available

Effective; support for data abstraction mainly from the language itself, and object-oriented needs further support from the programming environment. For versatility, support

According to abstract and object-oriented languages, hardware must be utilized efficiently.

7. Acknowledgments

This article is from the earlier version of the Association of Simula Users meeting in Stockholm. The discussion in there has led to this

The style and content of the text have made a lot of improvements. Brain Kernighan and Ravi Sethi have given a lot of constructive comments. At the same time, I thank all to enhance

C made a contributing person.

8. References

Slightly, please refer to the original text

转载请注明原文地址:https://www.9cbs.com/read-28231.html

New Post(0)