Objective C ++, 3rd edition, item 27: Minimal casting (forced transformation) (I)

Source: Internet
Author: User

Item 27: Minimal casting (forced transformation)

By Scott Meyers

Translator: fatalerror99 (itepub's nirvana)

Release: http://blog.csdn.net/fatalerror99/

C ++ rules are designed to ensure that no type errors will occur. Theoretically, if your program wants to compile smoothly, it should not try to perform any unsafe or meaningless operations on any objects. This is a very valuable guarantee, and you should not give up easily.

Unfortunately, casts (forced transformation) disrupts the type system ). It can cause a variety of troubles, some of which are easy to detect, and others are exceptionally subtle. If you switch from C, Java, or C # To C ++, please note that Because casting (forced transformation) is more necessary in those languages than in C ++, less dangerous. However, C ++ is neither c nor Java nor C #. In this statement, casting (forced transformation) is a feature that requires you to have a great sense of reverence.

Let's start by looking back at the casting syntax, because the same cast (forced transformation) is usually written in three different ways. C-style casts (forced transformation of c style) is as follows:

(T)Expression// CastExpressionTo be of type T

Function-style casts (forced transformation of function style) uses the following syntax:

T (Expression) // CastExpressionTo be of type T

There is no difference in meaning between the two forms. It is simply a question of where brackets are placed. I call these two formsOld-style casts(Forced transformation of old style ).

C ++ also provides four new cast (forced transformation) FormsNew-style(New Style) orC ++-style casts(Forced transformation of c ++ style )):

Const_cast <T> (Expression)
Dynamic_cast <T> (Expression)
Reinterpret_cast <T> (Expression)
Static_cast <T> (Expression)

Each option is applicable to a specific purpose:

  • Const_cast is generally used to forcibly remove constness (constant) of objects ). It is the only C ++-style cast (forced transformation of c ++ style) that can achieve this ).
  • Dynamic_cast is mainly used to execute "Safe downcasting" ("secure downward transformation"), that is, to determine whether an object is a specific type in an inheritance hierarchy (inheritance system. It is the only cast (forced transformation) statement that cannot be executed using the old-style syntax ). It is also the only cast (forced transformation) that may have a high runtime cost ). (I will provide details related to this later .)
  • Reinterpret_cast is the casts (forced transformation) that is specially used at the underlying layer to result in implementation-dependent (dependency implementation) (that is, cannot be transplanted). For example, a pointer is transformed into an int. Such a casts (forced transformation) should be extremely rare outside of the underlying code. I used it only once in this book and discussed how you should write a debugging Allocator for raw memory (bare memory) (refer to item 50 ).
  • Static_cast can be used for force implicit conversions (forced implicit conversion) (for example, from non-const object to const object (like in item 3), int to double, and so on ). It can also be used to execute reverse conversions for most of these conversions (for example, void * pointer to a type pointer, pointer-to-base (base class pointer) to pointer-to-derived (derived class pointer), although it cannot convert const objects to non-const objects. (Only const_cast can do this .)

Old-style casts (forced transformation of old style) is still legal, but new forms are more desirable. First, they are easier to recognize in the Code (both for humans and for tools like grep), which simplifies the search for Type Systems (type systems) in the code) the process of being disturbed. Second, it is more accurate to specify the purpose of each cast (forced transformation), making it possible for the compiler to diagnose usage errors. For example, if you try to use a new-style cast (New Style forced transformation) other than const_cast to eliminate constness (constant), your code will not compile.

When I want to call an explicit Constructor (explicit constructor) to pass an object to a function, it is probably that I only use the old-style cast (forced transformation of the old style). For example:

Class widget {
Public:
Explicit widget (INT size );
...
};

Void dosomework (const widget & W );

Dosomework (Widget (15)); // Create widget from int
// With function-style cast

Dosomework (Static_cast <widget> (15)); // Create widget from int
// With C ++-style cast

For some reason, the methodical object creation (Object creation) does not feel like a cast (forced transformation), so in this case, I may use function-style cast (forced transformation of function style) replace static_cast. Also, when you write the code that causes core dump, you usually feel that you have a perfect reason, so you 'd better ignore your feelings and always use new-style casts (New Style forced transformation ).

Many Programmers think that casts (forced transformation) does nothing but tells the compiler to regard one type as another, but this is wrong. Any kind of type conversion (whether through casts (forced transformation) explicit or compiler-added implicit (implicit) often leads to executable code at runtime. For example, in this code snippet,

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

Cast (forced transformation) between int X and double naturally generates code, because in most system architectures, the underlying representation of an int is different from that of a double. This may not be surprising, but the following example may give you a little bit of attention:

Class base {...};

Class derived: public base {...};

Derived D;

Base * pb = & D; // implicitly convert derived * → base *

Here we just created a base class pointer (base class pointer) pointing to a derived class Object (derived class Object), but sometimes the values of these two pointers are different. In this case, an offset will be applied to the derived * pointer at runtime to get the correct base * pointer value.

The next example shows that a single object (for example, an object of the derived type) may have more than one address (for example, it is directed by a base * pointer and its address directed by a derived * pointer ). This will not happen in C, nor in Java, nor in C #. It will only happen in C ++. In fact, if multiple inheritance (Multi-inheritance) is used, it will certainly happen, but it will also happen under single inheritance (single inheritance. When combined with other things, it means you should avoid making assumptions about how C ++ places things. Of course, you should not execute casts based on these assumptions (forced transformation ). For example, forcing an object's address to be converted to a char * pointer and then using pointer operations will almost always lead to undefined behavior (undefined behavior ).

However, please note that an offset is "sometimes" required. The objects placement method and the computed method of their addresses vary between different compilers. This means that your "I know how things are placed" casts (forced transformation) can work on one platform, not that they can work on other platforms. This world is filled with poor programmers who have learned this experience through a painful path.

One interesting thing about casts (forced transformation) is that it is easy to write things that seem right (maybe right in other languages. For example, many application frameworks require the implementation of virtual member functions (Virtual member functions) in Derived classes (derived classes) to call their base class (base class) first). Suppose we have a window base class (base class) and a specialwindow derived class (derived class). They all define the virtual function (virtual function) onresize. It is further assumed that the onresize of specialwindow is expected to call the onresize of window first. This is a method to achieve this. It seems correct and not true:

Class window {// base class
Public:
Virtual void onresize () {...} // base onresize impl
...
};

Class specialwindow: public window {// derived class
Public:
Virtual void onresize () {// derived onresize impl;
Static_cast <WINDOW> (* This). Onresize (); // cast * This to window,
// Then call its onresize;
//This doesn' t work!

... // Do specialwindow-
} // Specific stuff

...

};

I highlighted cast in the Code (forced transformation ). (This is a new-style cast (forced transformation of the new style), but using an old-style cast (forced transformation of the old style) does not help .) As you expected, the Code forcibly transforms * this into a window. Therefore, the onresize call result is window: onresize. You may not expect it to call that function on the current object (current object! As an alternative, cast creates a new temporary * this base class part (base class part)Copy(Copy), and then call onresize on this copy! The above Code does not call window: onresize on the current object (current object), and then executes the specialwindow-specific action on this object-it is in the current object (current object) before the specialwindow action is executedCopy of the base class partWindow: onresize is called on (copy of the base class. If window: onresize changes current object (current object) (the possibility is not small, because onresize is a non-const member function (member function), current object (current object) it will not change. As an alternative, one copy of that objectCopy(Copy) is changed. However, if specialwindow: onresize changes the current object (current object), the current object (current object) will be changed, resulting in the situation that the Code makes the current object (current object) it is a kind of pathology, but it does not change the base class (base class), but it does change the derived class (derived class.

The solution is to eliminate cast (forced transformation) and replace it with what you really want to express. You do not need to cheat the compiler to treat * this as a base class Object (base class object). You need) onresize base class version (base class version ). This is the case:

Class specialwindow: public window {
Public:
Virtual void onresize (){
Window: onresize ();// Call window: onresize
... // On * This
}
...

};

This example also shows that if you find that you want to perform cast (force transformation), this is a signal that you may use the wrong method to handle something. This is especially true when you want to use dynamic_cast.

(This film is not complete. Click here, next)

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.