Effective C ++ 2e Item18

zhaozj2021-02-11  209

Categories and functions: design and statement

Declaring a new class in the program will result in a new type: the design is the type design. Maybe you don't have much experience on type design, because most languages ​​have no opportunities to provide you. In C , this is very basic, not because you want to do this, but because you declare a class, you are actually doing, no matter what you want to do.

Design a good class is very challenging because the type of design is very challenging. Good types have natural grammar, intuitive semantics and efficient implementation. In C , the definition of a bad class is unable to implement these goals. Even if the performance of a member function of a class is also determined by the declarations and definitions of these member functions.

So how do you start designing efficient classes? First, you must know what you face. In fact, it will encounter the following problems when designing each class. Its answers will affect your design.

· How will objects created and destroyed? It will greatly affect the design of the constructor and the designer function, as well as the custom Operator New, Operator New [], Operator Delete, and Operator Delete []. (Terms M8 describes the differences of these terms)

· What is the difference between object initialization and object assignment? The answer determines the behavior of the constructor and the assignment operator and the differences between them.

· What does it mean by value to pass a new type of object? Remember, the copy function is responsible for answering this.

· What is the limit on the new type of legitimate value? These restrictions determine the types of errors inside the member function (especially constructor and assignment operators). It may also affect the exceptions of the exceptions thrown and exception specification for functions (see Terms M14), if you use them.

· Is the new type in line with inheritance relationship? If it is inherited from existing classes, then the design of new categories is limited to these classes, especially those that are limited to inherited are virtual or non-virtual. If the new class allows for inheritance of other classes, this will affect whether the function is to be declared as virtual.

· Which type of conversion is allowed? If the object implicitly converted to type B is implicitly converted to type B, it is necessary to write a type conversion function in class A, or write a non-Explicit constructor that can be called with a single parameter in class B. If you only allow explicit conversion, you want to write a function to perform a conversion function, but you don't have to write them into a type conversion operator and a non-Explicit constructor of the single parameter. (Terms M5 discuss the advantages and disadvantages of user-defined conversion functions)

· What operators and functions make sense to new types? The answer determines what function will be declared in the class interface.

· Which operators and functions should be explicitly banned? They need to be declared as private.

· Who has the right to access a new type of member? This problem helps to decide which members are public, which is protection, which private. It also helps to determine which classes and / or functions must be friends, and whether to nested a class to another class.

· How is the versatility of new types? Maybe you don't actually define a new type, but define a set of types. If so, don't define a new class, to define a new class template.

These are difficult to answer questions, so defining a efficient class is far less simple in C . But if you do it well, the type generated by the user-defined classes in C will almost no difference in the fixed type. If you can achieve this effect, its value is reflected.

Each question is to be monitored if you want to discuss with a detailed discussion. Therefore, the guidelines described in the following clauses will never be part of it. However, they emphasize some important precautions in the design, remind some frequent mistakes, and provide solutions to some problems that designers often meet. Many recommendations apply to non-member functions and member functions, so I also consider the design and statements of the function in the global function and the namespace. Terms 18: Strive for the complete interface and minimal

The user interface of the class refers to the interface that the programmer who uses this class can access the interface. Only functions exist in a typical interface, because there is a lot of shortcomings in the user interface (see clause 20).

Which functions should be placed in the interface of the class? Sometimes this problem will make you crazy, because there are two distant goals to be done. On the one hand, the class is designed to be easy to understand, easy to use, and easy to implement. This means that the number of functions should be as small as possible, and each function completes different tasks. On the other hand, the functionality of the class is powerful, it is convenient to use, which means adding functions from time to time to provide support for various general functions. How would you decide which functions should be put into the class, what do you not put?

Try this suggestion: The target of class interface is complete and minimal.

A complete interface refers to an interface that allows users to do any reasonable things they want to do. That is to say, there is a reasonable way to achieve any reasonable tasks that the user want to complete, even if this method is even more convenient to the user. Instead, a minimal interface means that the function is as small as possible, and each two functions do not have an overlapping function. If you can provide a complete, minimum interface, users can do anything they want, but the interface of the class does not have to be more complicated.

The complete look of the pursuit of interface is natural, but why should the interface minimize? Why don't you let users do anything they want, add more functions, so that everyone is happy?

Factors in the principle of the world do not talk - do you really do your users really correct? - The interface that is full of a large number of functions has a lot of shortcomings. First, the more functions in the interface, the more difficult to understand the potential users. The more disadvantageous understanding, the more I don't want to learn how to use it. A class with 10 functions is easy to use for most people, but a class with 100 functions is difficult to control many of the programmers. When the functionality of the extended class makes it as much as possible, be careful not to combat user learning to use their enthusiasm.

The big interface will also be confused. Assuming a class that supports identification functions in a manual smart program. One of the member functions called Think, and some people wanted to call the function name to Ponder (thoughtfully), and some people like to call Ruminate (meditation). In order to meet the needs of everyone, you have three functions, although they do the same thing. So think about it, what will I think about using this class in the future? This user will face three different functions, each function seems to be the same thing. really? What is the subtle difference in these three functions, efficient, versatility, or reliability? If there is no different, why do you have three functions? In this case, this user will not only grateful you provide the flexibility, will you wonder if you want (or think deeply, or meditating)?

The second disadvantage of a large class interface is difficult to maintain (see Terms M32). The class containing a large number of functions is more difficult to maintain and upgrade the class containing a small amount of functions, which is more difficult to avoid duplication of code (and repeated bugs), and it is difficult to maintain the consistency of the interface. At the same time, it is difficult to establish a document.

Finally, long class definitions will cause long header files. Because the program reads the header file in each compile (see Terms 34), the definition of the class will lead to a lot of compile time during the project development process. In summary, it is said that there is no price in the interface, so it is necessary to consider it carefully when adding a new function: the convenience it brings (only a new function should be considered if the interface is complete. Whether to provide convenience exceeding the additional cost it brings, such as complexity, readability, maintainability, and compilation.

But there is no need to be too embarrassed. Add some functions on the smallest interface is sometimes reasonable. If a universal function is implemented more efficient, this will add a good reason to add it to the interface. (However, sometimes it will not, see the Terms M16) If a member function is added, it is easy to use, or the user error can be prevented from adding it into an interface.

Look at a specific example: a class template, implements the array function of the user-defined subscript upper limit, and provides the upper and lower limit check options. The beginning of the template is as follows:

Template Class Array {public: enum boundscheckingstatus {no_check_bounds = 0, check_bounds = 1};

Array (int lowbound, int highbound, bitchcheckingstatus check = no_check_bounds);

Array (Const Array & RHS);

~ Array ();

Array & Operator = (Const Array & RHS);

Private: int lbound, hbound; // lower limit, upper limit

Vector data; // array content; for details, //, see Terms 49

Boundscheckingstatus checkingbounds;

The membership function that is currently declared is basically not wanting to think (or think deeply, meditation). A constructor allows the user to determine the constructor, a copy constructor, an assignment operator, and a destructor. The designer function is declared as non-virtual, meaning that this class will not be used as a base class (see clause 14).

For the declaration of assignment operators, the first look will feel that the purpose is not so clear. After all, the array of fixed types in C is not allowed to assign values, so it should not be allowed to assign a value (see Terms 27). But on the other hand, the array of vector templates (existing in the standard library - see Terms 49) allows the VECTOR object assignment. In this example, it is decided to follow the provisions of the Vector, as will be seen below, this decision will affect other parts of the interface of the class.

Old C programmer saw this interface to be scared: How can I not support fixed-size array declarations? It is easy to add a constructor to achieve:

Array (int size, bitchcheckingstatus check = no_check_bounds);

But this cannot be a minimum interface, because the constructor with the upper and lower limit parameters can complete the same thing. Despite this, it is possible for some purposes to cater to those long-term needs, especially for considerations consistent with the basic language (C language). What functions do you still need? For a complete interface, it is of course also required indexes of array:

/ / Return Elements T & Operator [] (int index) that can be read / written;

/ / Return to read-only Element Const T & Operator [] () const;

By declaring the same function twice, a CONST has no const at once, providing support for Const and non-const array objects. The return value is very important, and the terms 21 will be described.

Now, the Array Template supports constructor, destructuring functions, pass values, assignments, indexes, you may think of this is already a complete interface. But look at it again. If a user wants to traverse an integer array, print each element, as shown below:

Array A (10, 20); / / The lower limit of the subscript is: 10 to 20

...

For (int I = A lower limit; i <= a subsurdeal upper limit; i) cout << "a [" << i << "] =" << a [i] << '/ N ';

How did the user get a subscript of A? The answer depends on what is the assignment operation of the Array object, that is, what is done in Array :: Operator =. In particular, if the assignment can change the upper limit of the Array object, it must be provided with a member function that returns the current upper limit value because the user cannot always launch the upper and lower limits in some places of the program. For example, as the above example, a is assigned to the time period before the cycle is defined, and the user cannot know the current up and down limit in the cycle statement.

If the up and down limit of the Array object cannot be changed, it is fixed when A is defined, and the user may have a way (although it is very troublesome) track it. In this case, it is convenient to provide a function to return the current upper and lower limit, but the interface cannot be minimized.

Continue the previous assignment operation can change the hypothesis of the upper limit of the object, the upper and lower limit functions can be declared:

INT lowbound () const; int highbound () const;

Because these two functions do not have any modifications to their objects, they are declared as a Const member function for the principles of the objects they are located. With these two functions, the circular statement can be written like this:

For (int i = a.lowbound (); i <= a.highbound (); i) cout << "a [" << i << "] =" << a [i] << '/ N ';

Of course, to work such an object array of object arrays such as an operation type T, it is necessary to define an Operator << function for the object of the type T. (It is not quite accurate. It should be, there must be a type T of Operator <<, or, t can implicitly convert (see Terms M5) other types of Operator <<)

Some people will argue, and the Array class should provide a function to return the number of elements in the Array object. The number of elements can be simply obtained: highbound () - lowbound () 1, so this function is not so really necessary. But considering that many people often forget " 1", increasing this function is not a bad idea. There are also other functions that can be added to the class, including those operations in the input and output, as well as various relational operators (for example, <,>, ==, etc.). However, these functions are not part of the minimum interface, as they can be implemented by a loop containing Operator [].

Specifies the functions such as Operator <<, Operator >>, and the Terms 19 explains why they often use non-members' friend functions without member functions. Also, don't forget that the friend's function is part of the interface of the class in all practical applications. This means that friend functions affect the integrity and minimum of the interfaces.

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

New Post(0)