Effective C ++ 2e Item21

zhaozj2021-02-11  222

Terms 21: Use const as possible

The benefit of using const is that it allows you to specify a language constraint - some object cannot be modified - the compiler is specific to implement this constraint. With const, you can notify the compiler and other programmers to remain unchanged. As long as this is this, you will use const as a clear, because doing so can make sure that this constraint is not destroyed with the help of the compiler.

The Const keyword is really unity. Outside the class, it can be used for global or name spatial constants (see clauses 1 and 47), as well as static objects (local objects within a certain file or block range). Inside the class, it can be used for static and non-static members (see clause 12).

For pointers, you can specify the pointer itself as const, or you can specify the data referred to by the pointer to const, or both at the same time specified as const, and both, the two do not specify as const:

Char * p = "hello"; // Nonconst pointer, // non-Const data

const char * p = "hello"; // Nonconst Pointer, // Const Data

Char * const p = "hello"; // constant pointer, // non-Const data

Const char * const p = "hello"; // const pointer, // const data

The syntax does not seem to change the multi-end. In general, you can draw a vertical line in the mind through the asterisk (*) position in the pointer statement, if constant appears on the left side of the line, the data pointed to by the pointer is constant; if consts appears online right, the pointer itself Constant; if both of the CONST online appears, both are constants.

In the case where the pointer is referred to as constant, some programmers like to put the const before the type name, and some programmers like to put the const after the type name, the asterisk. Therefore, the following functions are the same type of parameter:

Class widget {...};

Void F1 (const widget * pw); // F1 takes a pointer to // widget constant object

Void F2 (Widget const * pw); // with F2

Because both representations exist in the actual code, they are used to these two forms.

Some powerful features of Const are based on its application in the function declaration. In a function declaration, const can refer to the return value of the function, or a parameter; for the member function, it can also refer to the entire function.

Let function return a constant value can often reduce the chance of making mistakes without reducing security and efficiency. In fact, as explained in Terms 29, it is possible to improve the security and efficiency of a function on the return value, otherwise there will be a problem.

For example, see this declaration of the reasonable Operator * function introduced in terms 19:

Const Rational Operator * (Const Rational & LHS, Const Rational & RHS); Many programmers first see it will wonder: Why is the return result of Operator * Is a const object? Because if this is not the case, the user can do the following bad things:

Rational A, B, C;

...

(a * b) = c; // assign values ​​for the results of A * B

I don't know why some of the programmers will assign a value directly to the two number of calculations, but I know: If A, B, and C are fixed types, this is obviously inevitable. A nice user-defined type is characteristic that it avoids that unequality is not compatible with fixed types. For me, it is very unreasonable to assign a value for two counting operations. Declare the return value of Operator * to prevent this, so this is correct.

There is nothing special about const parameters to emphasize - their operations are the same as part of the local const. (However, see the clause M19, the const parameter will result in a temporary object production) However, if the member function is const, it is another thing.

The purpose of the Const member function is of course to indicate which member function can be called on the Const object. But many people ignore such a fact that only a different member function can be overloaded only in Const. This is an important feature of C . Take later this String class:

Class string {public:

...

// Operator [] char & operator [] (incount) for non-const objects [];

// Operator [] const char & operator []} "for constradis;};

PRIVATE: CHAR * DATA;

String S1 = "Hello"; cout << S1 [0]; // Call non-const // string :: operator [] const string s2 = "world"; cout << S2 [0]; // call const // String :: Operator []

By overloading Operator [] and gives different versions of different versions, CONST and non-Const String can be different:

String s = "Hello"; // Nonconst String object

Cout << s [0]; // correct - read one // non-const string

s [0] = 'x'; // correct - write one // non-const string

Const string cs = "world"; // const string object

COUT << CS [0]; // correct - read one // const stringcs [0] = 'x'; // error! - Write a // const string

Also note that the error here is only related to the return value of the call Operator []; Operator [] call itself. The reason for the error is that attempts to a const char & assignment, because the object being assigned is the return value of the Const version of the Operator [] function.

Also note that the return type of non-Const operator [] must be a reference to a char, CHAR itself can't. If Operator [] really returns a simple char, the statement as shown below will not be compiled:

s [0] = 'x';

Because the return value of the function to modify a "return value is fixed type" is absolutely illegal. Even if the C "is returned to the internal mechanism of the object" (instead of reference), S.DATA [0] is modified, not S.Data [0] This is not the result you want.

Let us stop and see a basic principle. What is the exact meaning of a member function for Const? There are two main views: Const (Bitwise Constness) and Concept (Conceptual Constness) in the Const (Bitwise Constness) and conceptual sense.

Bitwise Constness persists, this member function is constant when and only when the member function does not modify any data member (except static data members), that is, when any bit (bit) is not modified. Bitwise Constness is the most advantageous thing to easily detect events that violate Bitwise Constness: The compiler is only used to find the assignment of a non-paid data member. In fact, Bitwise Constness is the definition of C to Const issues, and the Const member function is not allowed to modify any of its objects.

Unfortunately, many members of the Bitwise Constness definition can also be tested through Bitwise. In particular, a member function that "modify the data pointed to by the pointer", its behavior clearly violates the Bitwise Constness definition, but if the object contains only this pointer, this function is also bitwise const, compile time. This is different from our intuition:

Class string {public: // Constructor, make Data points to copy string (const char * value) of data pointed at by // Value;

...

Operator char * () const {return data;}

PRIVATE: CHAR * DATA;

const string s = "hello"; // Declaration constant object

Char * Nasty = S; // Call Operator Char * () Const

* Nasty = 'm'; // Modify S.Data [0]

Cout << S; // Output "Mello"

Obviously, if you have a constant object with a value and call the object's Const member function, there must be any errors, the value of the object can be modified! (About this example is discussed in more detail in detail 29) This results in the introduction of the Conceptual Constness view. The persistence of this point of view believes that a const member function can modify some of the data (BITS) of its object, but only if the user will not be found. For example, assume that the String class wants to save the object length each time the object is requested:

Class string {public: // Constructor, make Data points to copy string (const char * value): lengthsvalid (false) {...}

...

SIZE_T longth () const;

PRIVATE: CHAR * DATA;

SIZE_T DATALENGTH; / / Finally calculated / string length

Bool lengthisvalid; // Current // is legal};

SIZE_T STRING :: Length () const {if (! lengthisvalid) {datalength = strlen (data); // error! Length; = true; // errors!}

Return Datalength;}

This Length's implementation is clearly not in line with the definition of "Bitwise Const" - Datalength and LengthsValid can be modified - but for Const String objects, it seems to be legitimate. But the compiler does not agree, they insist on "Bitwise Constness", what should I do?

Solution is simple: use C Standard organizations to specifically provide another optional option for Const Problems for this type. This scenario uses a keyword Mutable. When the non-static data member uses Mutable, these members' "bitwise constness" restrictions are released:

Class string {public:

... // Same as Above

PRIVATE: CHAR * DATA;

Mutable size_t datalength; // These data members are now // for mutable; they can be modified in Mutable Bool Length, and anywhere, even in the Const member function.

SIZE_T STRING :: Length () const {if (! lengthisvalid) {datalength = Strlen (data); // Now legthiThisvalid = true; // equally legal}

Return Datalength;}

Mutable is a good solution when dealing with the "Bitwise-Constness" problem, but it is not long in the C standard, so some compiler may not support it. If so, you have to go back to C dark old age, where, life is very simple, and constant may be abandoned.

Among a member function of class C, the THIS pointer is as follows: c * const this; // Non-Const member function

Const C * const this; // Const member function

In this case (ie, the compiler does not support Mutable), if you want to make the problem with the String: Length version of the Const and non-Const objects, only the THIS is changed from Const C * Const. C * const. It cannot be done directly, but can be indirectly implemented by initializing a local variable pointer to indirectly implement the same object referred to by this. Then, you can access the members you want to modify through this partial pointer:

SIZE_T STRING :: Length () const {// Define a THIS pointer string * const directly to the // local version of the Const object * const directly * const> (this);

IF (! lengthisvalid) {localthis-> datalength = strlen (data); localthis-> length;}

Return Datalength;}

Don't be very beautiful. But in order to complete the desired function, only do it.

Of course, if this method cannot be guaranteed, don't do this: For example, some old "eliminating const" methods will not work. In particular, if the object refers to this is really const, that is, when it is defined as const, "eliminating const" will result in unsureable consequences. So, if you want to eliminate Const in a member function, it is best to confirm that the object you want to convert is not defined as const.

In other cases, the CONST will be both useful and safe through the type conversion. This is: Pass a const object to a function that takes a non-const parameter, and you know that the parameters will not be modified inside the function. The second condition is important because the object that is only read (not written) eliminates constant, even if the object is initially defined as const.

For example, I already know that some libraries do not correctly declare the Strlen functions below:

SIZE_T STRLEN (CHAR * S);

Strlen, of course, will not modify the data referred to S - at least I have never seen it in a lifetime. But because there is this statement, it will not be legal when calling this function for a const char * type pointer. To solve this problem, you can securely convert this pointer's const forced CONST for Strlen to:

Const char * klingongreeting = "nuqneh"; // "nuqneh", "hello" // size_t length = strlen (const_cast );

But don't abuse this method. It can ensure that it can work normally when the function (such as the strlen in this example) does not modify the data referred to in its parameters.

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

New Post(0)