Objective C ++ Study Notes-Article 27: Objective Java notes

Source: Internet
Author: User

Objective C ++ Study Notes-Article 27: Objective Java notes

* *********************************** Please specify the source: bytes ********************************************





V. Implementations



 

Rule 27: Minimize casting

Rule 27: Minimize transformation actions




1. Some basics

One of the design goals of C ++ rules is to ensure that "type errors" are absolutely impossible.

Theoretically, if a program is compiled cleanly, it means that it does not attempt to perform any insecure, meaningless, silly, and absurd operations on any object.

However, cast destroys the type system, which may lead to troubles of any kind, and these troubles are cumbersome.

If you are using C, Java, or C #, pay special attention to this because the transformation in those languages is necessary and unavoidable. Compared with C ++, it is not especially dangerous.


There are usually three different forms of cast: (convert expression to T)

-- C-style transformation action: (T) expression

-- Function style transformation action: T (expression)

There is no difference between the two forms, but the positions of parentheses are different. The two forms can be called "Old-style transformation"

C ++ also provides four new transformations (often called new-style or C ++-style)

-- Const_cast <T> (expression)

This type is usually used to convert the object's constants to Division. It is also the only C ++-style transformation operator with this capability.

-- Dynamic_cast <T> (expression)

It is mainly used to perform "Security downward transformation", that is, to determine whether an object belongs to a type in the inheritance system. It is the only action that cannot be executed by the old syntax and the only transformation action that may consume significant operation costs.

-- Reinterpret_cast <T> (expression)

When performing a low-level transformation, the actual action (and result) may depend on the compiler, which means it cannot be transplanted. For example, convert a pointer to int to an int. This type of transformation is rare outside of low-level code.

-- Static_cast <T> (expression)

It is used to perform forced implicit conversion. For example, converting a non-const object to a const object or converting an int to a double object. It can also be used to perform reverse conversions of the preceding conversions, such as converting the void * pointer to the typed pointer and converting the pointer-to-base to the pointer-t0-derived. However, it cannot convert the const to non-const, which can only be obtained by const_cast.


Although the old-style transformation is still legal, the new-style transformation is quite popular. There are two reasons:

> They are easily identified in the Code (whether manually identified or using tools), thus simplifying the process of "identifying where the type system is destroyed.

> The narrower the targets of each transformation action, the more likely the compiler will diagnose the wrong application.

PS: There is a unique time to use the old transformation. When you need to call an explicit it constructor to pass an object to a function.

For example:

Class Widget {public: explicit Widget (int size );...}; void doSomeWork (const Widget & w); // create a WidgetdoSomeWork (Widget (15) with an int plus a transformation action of "function style )); // create a WidgetdoSomeWork (static_cast <Widget> (15) with an int plus a "C ++ style" transformation action ));





2. Something

① Many Programmers think that transformation is not actually done, but they just tell the compiler to regard a type as another type. But, this is a wrong idea.

Any type conversion always really requires the compiler to compile the code executed during the runtime.

For example, in the following program:

Int x, y;... double d = static_cast <double> (x)/y; // divide x by y and use floating point division.

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

In the following example, pay special attention to the following:

Class Base {...}; class Derived: public Base {...}; Derived d; Base * pb = & d; // The metaphor for converting Derived * to Base *

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

The preceding example shows a single object (for example, an object of the Derived type) there may be more than one address (for example, the address when "Base * points" and "Derived * points"), which cannot happen in C, Java, and C, only in C ++ can. In fact, once multiple inheritance is used, it will always happen, even in a single inheritance. Although there may be some other things, it at least means that we should avoid the assumption of "how to deploy objects in C ++, we should not perform any transformation actions based on this assumption.

② It is easy for us to write some plausible code (even in other languages ).

For example, many application frameworks require that the first action of the virtual function code in the derived class call the corresponding function of the base class. Suppose we have a Window base class and a SpecialWindow derived class, both of which define the virtual function onResize. Further assume that the onResize function of SpecialWindow is required to receive the onResize that calls Window first. Below is an implementation method (like but not like)

Class Window {// base class public: virtual void onResize (){...}...}; class SpecialWindow: public Window {// derived class public: virtual void onResize () {static_cast <Window> (* this ). onResize (); // convert * this to Window and call its onResize. this is wrong ...}...};

This Code emphasizes the transformation actions (New transformations are used here ). This program converts * this to Window, and CALLS Window: onResize to the onResize function. However, it calls not the function of the current object, but the onResize on the temporary copy of a "* this object's base class component" created by the earlier transformation action.

The solution to this problem is to remove the transformation action and let us go.

For example, if you only want to call the onResize function of the base class version, make it act on the current object. So write:

Class SpecialWindow: public Window {public: virtual void onResize () {Window: onResize (); // call Window :: onResize acts on * this ...}...};

In this example, we can find that if you want to use the transformation action, this is equivalent to a warning, because it may be moving the situation to the wrong direction, especially when using dynamic_cast.

③ For dynamic_cast, the execution speed of many of its implementation versions is quite slow. For example, there is at least one common implementation version based on "class Name string comparison". If you execute dynamic_cast on an object in the layer-4 deep single inheritance system, each dynamic_cast provided by the implementation version just mentioned may use strcmp calls up to four times to compare the class name. The cost of deep inheritance or multi-inheritance is higher!

When should I use dynamic_cast? Dynamic_cast is usually used because you want to execute the derived class operation function on an identified derived class object, but there is only one pointer or reference to the base class.

There are two ways to avoid this problem.

> Using a container and storing the pointer directly pointing to the object of the derived class (usually a smart pointer) eliminates the need to "process the object through the base class interface.

In the previous Window example, if only SpecialWindow in the Window/SpecialWindow inheritance system supports the 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 != winPtrs.end() ; ++iter )  {  if( SpecialWindow* psw = dynamic_cast<SpecialWindow* >( iter->get() ) )    psw->blink();}

It should be like this:

Typedef std: vector <std: tr1: shared_ptr <SpecialWindow> VPSW; VPSW winPtrs ;... for (VPSW: iterator iter = winPtrs. begin (); iter! = WinPtrs. end (); ++ iter) {// dynamic_cast (* iter)-> blink ();}

Of course, this approach makes it impossible for you to store pointers in the same container to "point to all possible windows Derived classes ". If you really want to process multiple window types, more containers may be required, both of which must have type security.

> Another method 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. In this example, although only SpecialWindow can flash, it may make sense to declare the flickering function in the base class and provide a default implementation code of "nothing done:

Class Window {public: virtual void blink () {}// default implementation code ...}; class SpecialWindow: public Window {public: virtual void blink (){...}; // In the SpecialWindow class, the blink function performs some operations ...} typedef std: vector <std: tr1: shared_ptr <Window> VPW; // a container containing a pointer pointing to all possible windows VPW winPtrs ;... for (VPW: iterator iter = winPtrs. begin (); iter! = WinPtrs. end (); ++ iter) // dynamic_cast (iter *)-> blink () is not used here ();

No matter which method is used-"Use Type security container" or "move virtual functions above the inheritance system"-it is not universally applicable, however, in many cases, they provide a feasible dynamic_cast alternative.




3. Note

One thing to avoid- cascading dynamic_casts

That is, something like this:

Class Window {...};... // The derived class defines teypdef std: vector <std: tr1: shared_ptr <Window> VPW; VPW winPtrs ;... for (VPW: iterator iter = winPtrs. begin (); iter! = WinPtrs. end (); ++ iter) {if (SpecialWindow1 * psw1 = dynamic_cast <SpecialWindow1 *> (iter-> get ())){...} else if (SpecialWindow2 * psw2 = dynamic_cast <SpecialWindow2 *> (iter-> get ())){...} else if (SpecialWindow3 * psw3 = dynamic_cast <SpecialWindow3 *> (iter-> get ())){...}...}

This code is large and slow, and the foundation is unstable, because every time the Window class inheritance system changes, all this type of code must be reviewed again to see if it needs to be modified.

For example, once a new derived class is added, a new conditional branch may be added to all the preceding conditions. Such code should be replaced by "call based on virtual functions.




4. Summary

Excellent C ++ Code rarely uses transformation, but it is unlikely to say that it is completely out of them. There are many transformations that are reasonable, although some do not have. As in the face of a variety of constructor functions, we should do as little as possible with the transformation action, usually hiding it in a function, the function interface will protect the caller from the internal interference of the function.

★Remember ☆

If possible, try to avoid transformation, especially to avoid dynamic_casts in code that focuses on efficiency. If there is a design that requires transformation, try to develop an alternative design that does not require transformation.

If transformation is necessary, try to hide it behind a function. Users can call this function later without putting the transformation into their own code.

We would rather use the C ++-style transformation than the old one. The new transformation is easy to identify and supports different categories.






* *********************************** Please specify the source: bytes ********************************************

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.