Effective C ++ 2e Item25

zhaozj2021-02-11  250

Terms 25: Avoid overloading pointers and digital types

Quickly answer: What is "zero"?

More specifically, what happens below the code?

Void f (int X); Void F (String * PS);

f (0); // Call F (int) or f (string *)?

The answer is that 0 is an int-- accurately, the integer constant on a literal - So, "Always" F (int) is called. This is the problem: because not all people always want it to do so. This is a unique situation in the C world: When people think that a call should be polymor, the compiler is not so dry.

If you want to use symbolic names (for example, null means NULL pointer) to solve such problems, but it is difficult to achieve more than imagination.

The first thing I thought should be to declare a constant called NULL, but constant has a type, what should NULL? It is to be compatible with all pointer types, but the only type of condition is Void *, and if you want to pass the VOID * pointer to a type of pointer, there must be an explicit type conversion. This is not only hard to see, but it is not better than the initial situation:

Void * const null = 0; // Possible NULL definition

f (0); / / still call f (int) f); // call f (string *) f (static_cast (0)); // Call F ( String *)

However, it is important to use null to represent a void * constant method is still better than initial, because if it is guaranteed to use null to indicate Null pointer, it is possible to avoid ambiguity:

f (0); // Call f (int) f (null); // error! - Type does not match F (static_cast (null)); // correct, call f (string *)

At least a "F function) that has been previously modified (0 calls" error) is now shifted into a compile time (passing a void * to the string * parameter). The situation is slightly improved (see Terms 46), but what you need to conversion is still annoying.

If you want to be shameless to return to help, you will find it can't solve the problem, because the most obvious way is nothing more than:

#define null 0

or

#define null ((void *) 0)

The first way is just the literal 0, essentially a constant constant (if you remember, or the initial problem); the second method will pull you back to "Biography Void * pointer to some type What is the trouble of pointer.

If there is research on the rules of type conversion, you will know that C will consider "Conversion from long int 0 to NULL pointer" and "Convert from Long Int to Int", it is nothing wrong. So you can use this, introduce polysestation to the above place where you may think there is "int / pointer":

#define null 0L // Null is now a long int

Void f (int X); Void F (String * P);

f (null); // Error! - Ambiguity, when you want to override Long Int and pointers, it doesn't work:

#define null 0L

Void f (long int x); // This f is now the parameter of Longvoid F (String * P);

f (null); / / correct, call f (long int)

In actual programming, this is more secure than to define null as int, but it is not just transferring problems, not eliminating problems.

This problem can be eliminated, but you need to use the latest feature of the C language: Member Function Template (often referred to as a member template). As the name suggests, the member function template is a template that generates member functions within the class. Take the above discussion about NULL, we need a "Type of Types, which works like static_cast (0) expression". That is, the NULL becomes an object that "contains an implicit type conversion operator" class, which can be applied to each possible pointer type. This requires a lot of conversion operators, but they can help C generate from member templates:

// A first step design of a class that can generate NULL pointer objects Class NullClass {public: Template / / for all types of T * () const {return 0;} // generates Operator T *; }; // Each function returns a // NULL pointer //

Const nullclass null; // null is an object of type nullclass //

Void f (int x); //

Void F (String * P); //

f (null); // convert NULL to String *, // Then call f (String *)

This is a good preliminary design, but it can also improve from a few aspects. First, we actually only need a NULLCLASS object, so we have no need to give this class; we only need to define an anonymous class and make NULL a type. Second, since we want NULL to convert to any type of pointer, it must also be able to handle member pointers. This needs to define the second member template, its role is to convert 0 to type T C :: * (pointing to a member of class C) to all class C and all type T. (If you don't understand the member pointer, or you have never heard of it, or very little, it is not tight. The member pointer can be called rare animals, which is very rare, maybe many people have never used it. This curious person can refer to Terms 30, and the member pointer is discussed in detail.) Finally, to prevent the user from taking NULL address, because the behavior of NULL is not like a pointer, but the value of the pointer And the value of the pointer (such as 0x453AB002) is no address. Therefore, the definition of improved NULL looks like this:

Const // This is a const object ... Class {public: Template // can convert any type Operator T * () const // NURE Pointer {Return 0;} //

Template // can be converted to any type Operator T C :: * () const // NULL member pointer {return 0;}

Private: void Operator & () const; // Can't take your address / (see Terms 27)

} Null; // Name is NULL

This is the real code you see, although there is a name that may want to give classes in actual programming. If you do not give a name, the compiler pointing to the null type information is really hard to understand.

Another example of the usage of a member template See the Terms M28.

Important is that all of the above NULL designs that produce correct work is only meaningful when you are a caller. If you are a person designed to be called a function, write such a NULL that is used by others is actually not much, because you can't force your caller to use it. For example, even if you provide the NULL developed above, you can't prevent them from doing this:

f (0); / / still call f (int), // Because 0 or Int

It is also the same as the problem that appears in the forefront of this Territory.

Therefore, as the designer of the overload function, the most basic one in the final analysis is that as long as it is possible, it is necessary to avoid overloading a number and a pointer type.

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

New Post(0)