Reprinted please indicate the source: http://blog.csdn.net/wingfiring
Some C/C ++ programming books once mentioned how to judge whether the pointer is null. obviously, if (P = NULL), if (P = 0), and if (p) can both complete this task. The difference lies in readability. we will discuss them separately.
1. If (P = NULL)
A considerable number of articles are recommended. Some of them even think that other practices are incorrect. in this form, a variant is if (null = P). Although it is a little weird, in case we mistakenly write = As =, the compiler usually gives a compilation error, this prevents typing errors. those who support this scheme believe that null can clearly express P as a pointer rather than an integer or something else. helps us understand the code. however, there are also many opponents, including B, the father of C ++. s. the main objection is that null is not a part of the language. Generally, it is defined by a macro:
# Define null (void *) 0)
In general, null can actually work very well, but the macro is born to make it dislike people. in addition, it does have some practical problems, such as typical member pointers. assume there is a type,
Typedef void (A: * Funt )();
Now, Funt is defined as a member function pointer type. Suppose there is Funt fun =...
If (fun = NULL) will cause compilation failure. the reason is that there is no type conversion between Funt and void. this issue exists not only in member functions, but also in common function pointers.
So what about changing the definition of null? Try to define as follows:
# Define null (0)
Or
# Define null 0
This may also cause problems. null indicates that you are a pointer and only allow comparison between the pointer and null. this is suspected of deception. many people think that deception is unacceptable, and exposing problems is much better than deception. this spoofing causes the compiler to fail to provide proper help.
2. If (P = 0)
Many people, including B .s, also recommend this method, although most of them think this solution is not perfect. first, the language has special processing for constant 0. Therefore, the first option may solve the problem of comparing the pointer with a function or a member function. on the other hand, it does not emphasize that it must be compared with the pointer, so there is no spoofing. moreover, because this method is supported by the language, every C ++ programmer will be familiar with this method and will not feel difficult to understand. in addition, a new language improvement is in progress and provides a nullptr keyword for the language to be used specially for comparison with various pointers. however, the previous language implementation basically does not support this feature.
3. If (P)
Many C ++ geeks or senior users sneer at the first two solutions. they think this solution is correct, simple, and effective. in addition, this solution can be effectively applied to the so-called smart_ptr. solutions 1 and 2 often lead to potential insecure factors. suppose we now have a smart_ptr class. To support the first syntax, we need to define a global comparison function for it. however, this also means to provide a series of ugly (asymmetric parameter types) overload, and it takes a lot of effort. in addition, it is difficult to provide a completely consistent implementation.
Let's take a look at what different solutions need to do to support smart_ptr.
For solution 1, we need to define:
Bool operatro = (const type & L, void * null );
Bool operatro = (void * null, const type & R );
Likewise, operator needs to be reloaded! =;
The problem here is that we didn't expect to be equal to any void pointer in smart_ptr, but now we have to allow it. that is to say, we can compare it with a non-null void pointer. our intention is to query whether the pointer is null.
Solution 2 has the same problem and is more serious. We may compare it with any integer. For normal pointers and non-0 constants, compilation fails.
Another method is implicit conversion, but it is more insecure.
In the third solution, operator is often reloaded! And operator bool. Although it is also dangerous to overload operator bool, it can be bypassed technically now, that is, to reload an unspecified_boolean method, similar implementation is as follows:
Template <typename derived>
Class unspecified_boolean_imp
{
Struct tester {void dummy (){}};
Typedef void (Tester: * unspecified_boolean_type )();
Public:
Operator unspecified_boolean_type () const throw0 (){
Return static_cast <const derived &> (* This). Operator! ()? 0: & Tester: dummy;
}
Protected:
Unspecified_boolean_imp (){}
~ Unspecified_boolean_imp (){};
};
In this way, we can safely use the IF (p) syntax without worrying about misuse.
The third solution is common in GP code. however, those who support solution 1 and 2 often disagree that solution 3 is not conducive to reading, and is too skillful. 3 advocates believe that 3 is obviously a more pragmatic and effective method, which can make the implementation simple and clear and reliable.
So let's review our intention: Is the query pointer null? Why does it not provide a query function? Isn't it good to encapsulate the differences?
The following attempt is made:
If (is_nullptr (p ));
Well, I think this code is more straightforward than if (P = NULL). If is_nullptr is defined as a macro, it can be provided as follows:
# Define is_nullptr (x) = 0)
However, I don't like macros, and I think there are too few things that macro can do for me. For example, if p is an int, the compiler will succeed silently. I think it is obvious that is_nullptr should give a compilation error for non-pointer types. things fall onto the template head:
Template <typename T> bool is_nullptr (T * P ){
Return! P;
}
Now, I can write if (is_nullptr (p) with ease. If P is of the int type, compilation errors will occur. however, there are two other problems that cannot be solved. one is smart_ptr, and the other is a member function pointer. smart_ptr is actually very simple, just reload it
Template <typename T>
Bool is_nullptr (const smart_ptr <t> & P) {return! P ;}
With the help of traits technology to maintain the concept, in fact, we can do better, we only need a line of additional code, added in the definition of smart_ptr:
Enum {is_pointer = 1 };
That's enough. Even if we can't modify smart_ptr, we still have a back-to-back approach, isn't it? If the third-party smart_ptr does not support the syntax such as if (P), but by reloading is_nullptr, we can provide a unique form of use for all possibilities. I like this simplicity.
Function pointers are supported without special processing. Now we can process member function pointers:
Template <typename T, typename u>
Bool is_nullptr (t u: * X)
{
Return! X;
}
Fortunately, it can work. whether it is for member functions or member data. in addition, the parameter list of the function is ignored. people who have survived the implementation of the variable-length template parameter, do they think the world is unfair? Pai_^
For a function, t is not just a return value, but actually a function definition. However, I'm not sure whether it meets the ISO standards. But it doesn't matter. It's a big deal to go back to hell, I will be back!
Now, I can enjoy my static type security is_nullptr.