Negative tive C ++ Article 27: do as little transformation actions as possible

Source: Internet
Author: User

The transformation (casts) destroys the type system ). It may cause any type of trouble.

C ++ provides four new Transformations

Const_cast <t> (expression) // cast away the constness

Dynamic_cast <t> (expression) // safe downcasting Security downward Transformation

Reinterpret_cast <t> (expression) // the intention to perform a low-level transformation. The actual action (and result) may depend on the compiler, which also indicates that it cannot be transplanted. It is rare outside of the low-level code.

Static_cast <t> (expression) // used to force implicit conversion (implicit conversions)

C-style old-style transformation

(T) Expression

T (expression)

New transformations are quite popular. 1. They are easily identified in the Code. 2. The narrower the transformation actions, the more the compiler can diagnose the wrong application.

The only time to use the old transformation is when an explicit it constructor is called to pass an object to a function:

Class widget {
Public:
Explicit widget (INT size );
...
};
Void dosomework (const widget & W );
Dosomework (widget (15); // create a widget for function-style transformation actions
Dosomework (static_cast <widget> (15); // create a widget object for C ++ style transformation actions

The deliberate "Object generation" action is not very similar to "transformation". It is very likely that the function-style transformation action is used instead of static_cast. But sometimes it is best to ignore your feelings and use the new transformation rationally.

Any type conversion (whether it is the display conversion through the transformation action or the implicit conversion completed by the compiler) often makes the compiler compile the code executed during the runtime

Int X, Y;
...
Double D = static_cast <double> (X)/y; // use floating point Division

Converting an int to a double type almost certainly produces some code, because the underlying expression of an int is different from that of a double type in most computer architectures.

Class base {...};
Class derived: public base {...};
Derived D;
Base * pb = & D; // The metaphor for converting derived * to base *

Here we just create a base class pointer pointing to a derived class object, but sometimes the two pointer values are different. In this case, an offset is executed on the derived * pointer at runtime to obtain the correct base * pointer value.

The preceding example shows that a single object (for example, an object of the derived type) it may have more than one address (for example, the address when "base * points to it" and the address when "derived * points to it ). C, Java, and C # are not possible, but C ++ is possible! In fact, this is almost always happening once multiple inheritance is used. Even in a single inheritance. Although there are other meanings, it should at least mean that you should avoid making the assumption of "how to deploy objects in C ++", and should not perform any transformation actions based on this assumption. For example, converting an object address to a char * pointer and then performing pointer operations on them almost always results in meaningless (ambiguous) behavior.

The layout of objects and their address calculation methods vary with the compiler, which means that the transformation of an object is designed because it knows how the object is laid out, it may not work on other platforms.

Another interesting thing about transformation is that we can easily write some plausible code (maybe right in other languages ). For example, the onresize of specialwindow is required to call the onresize of window first. Which of the following seems true? Actually, the error is:

Class window {
Public:
Virtual void onresize (){...}
...
};
Class specialwinddow: public window {
Public:
Virtual void onresize (){
Static_cast <WINDOW> (* This). onresize (); // convert * this to a window, and then call its onresize; this will not work!
...
}
};

It calls not the function on the current object, but the onresize on the temporary copy of a "* base class component of this object" created in the earlier transformation! The specialwindow exclusive action is not executed on the current object after window: onresize is called. No, it calls window: onresize on the copy of "base calss component of the current object", and then executes the specialwindow exclusive action on the current object. If window: onresize modifies the object content, the current object is not modified, but the copy is changed. However, if you modify the object in specialwindow: onresize, the current object will be changed. This leads the current object to a "disability" State: its base class component changes have not been implemented, while the derived class component changes have been implemented.

The real solution is:

Class specialwinddow: public window {
Public:
Virtual void onresize (){
Window: onresize (); // call window: onresize to act on * This.
...
}
};

The execution speed of many dynamic_cast implementations is quite slow. If at least one common implementation version is compared based on the "Class Name string", the cost of deep inheritance or multi-inheritance is higher! Some implementation versions do this for some reasons (they must support dynamic links ). In code focusing on efficiency, we should be more alert and suspect about dynamic_cast.

The reason for using dynamic_cast is usually because you want to execute the derived class operation function on an object that you think is derived class, but you have only one pointer or reference pointing to the base, you can only rely on them to process objects. Two general practices can avoid this problem:

1. using containers and storing pointers directly to the derived class Object (usually smart pointers) eliminates the need to process objects through the base class interface. If specialwindows in the previous window/specialwindow inheritance system supports flickering effect, try not to do this:

Class window {...};
Class specialwindow: public window {
Public:
Void blink ();
};
Typedef STD: vector <STD: tr1: shared_ptr <WINDOW> vpw;
Vpw winptrs;
For (vpw: iterator iter = winptrs. Begin (); iter! = Vinptrs. End (); ++ ITER)
{
If (specialwindow * psw = dynamic_cast <specialwindow *> (ITER-> get ()))
Psw-> blink ();
}

It should be changed to the following:

Typedef STD: vector <STD: tr1: shared_ptr <specialwindow> vspw;
Vspw winptrs;
For (vspw: iterator iter = winptrs. Begin (); iter! = Vinptrs. End (); ++ ITER)
{
(* ITER)-> blink ();
}

Of course, this method cannot store the pointer "pointing to all possible windows Derived classes" in the same container ". If you need to process multiple window types, you may need multiple containers, all of which must be type-safe ).

Another way is to use the base class interface to process "all possible window Derived classes", that is, provide virtual functions in the base class to do what you want to do for various window Derived classes. Although only specialwindows can flash, it may make sense to declare the flickering function in the base class and provide a default version that does nothing:

Class window {
Public:
Virtual void blink (){}
};
Class specialwindow: public window {
Public:
Virtual void blink (){...};
};
Typedef STD: vector <STD: tr1: shared_ptr <WINDOW> vpw;
Vpw winptrs;
For (vpw: iterator iter = winptrs. Begin (); iter! = Vinptrs. End (); ++ ITER)
{
(* ITER)-> blink ();
}

Either method is not universally applicable, but in many cases they provide a feasible dynamic_cast alternative. When they do this, you should embrace them.

What must be rejected is the so-called "rolling (cascading) dynamic_casts ":

Typedef STD: vector <STD: tr1: shared_ptr <WINDOW> vpw;
Vpw winptrs;
For (vpw: iterator iter = winptrs. Begin (); iter! = Vinptrs. End (); ++ ITER)
{
If (specialwindow1 * psw1 = dynamic_cast <specialwindow1 *> (ITER-> get ())){...}
If (specialwindow2 * psw1 = dynamic_cast <specialwindow2 *> (ITER-> get ())){...}
If (specialwindow3 * psw1 = dynamic_cast <specialwindow3 *> (ITER-> get ())){...}
}

Such code should always be replaced by something called based on virtual functions.

Excellent C ++ Code rarely uses transformation. we should isolate transformation actions as much as possible, usually hiding it in a function, the interface of the function will protect the caller from any dirty callback action in the function.

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.