C + + type conversions

Source: Internet
Author: User
Tags mail exchange

An article from cplusplus.com, which is the most comprehensive, meticulous, and in-depth article I have seen about C + + type conversion. This article describes the various types of C + + conversions, including: implicit type conversion of basic types, C-style type conversion, implicit conversion of classes (implicit conversion), explicit keywords, static_cast, reintperet_cast, Const_cast, dynamic_cast. And the typeID keyword associated with rtti.

Original link (English): http://www.cplusplus.com/doc/tutorial/typecasting/


Implicit type conversions

(PS: First is the type conversion between built-in types)

Implicit conversions occur automatically when a value is copied to a compatible type. For example:

Short a = 2000;int b;b = A;
Here, the value of a is lifted from short to int and does not require any explicit operator, which is called standard conversion. The standard conversion acts on the base data type, allowing conversions between numeric types (short to int, int to float,double to int ...). ), turn or convert from bool, and some pointers.

To go from a smaller integral type to an int, or from float to double is called lift (promotion), it is guaranteed that the exact same value is generated in the target type. Transitions between other arithmetic types are not necessarily able to represent the absolute same value:

    • If you convert from a negative integer (PS: Signed) to an unsigned type, the resulting value corresponds to its binary complement representation
    • Transformation from (to) bool value, FALSE will be equivalent to 0 (for numeric type), or null pointer (null pointer) (for pointer type); all other values are equivalent to true and will be converted to 1
    • If you are converting from floating-point to Integer, the value will be truncated (the fractional part will be lost). Undefined behavior (undefined behavior) If the result exceeds the representation range of (target) type
    • In addition, if it is a conversion between numeric types of the same type (integer to Integer, or float-to-float), the transformation is legal, but the value is implementation-defined (IMPLEMENTATION-SPECIFIC,PS: compiler-implemented) and may result in non-portability. (PS: No one would do that)

These transformations are likely to result in loss of precision, which the compiler can produce warnings. These warnings can be avoided by an explicit conversion.

For non-primitive types, numeric values and functions can be implicitly converted to pointers, whereas pointers usually allow the following conversions:

    • Null can go to any type of pointer
    • Any type of pointer can be converted Void*
    • Pointer upward transformation: A derived class pointer can be converted to an accessible (accessible) and non-ambiguous (unambiguous) base class pointer without losing its const or volatile qualification.

Inter-class implicit conversions

In the world of classes, implicit conversions can be controlled by the following three member functions:

    • Single-argument constructors (single-argument constructors): Allows implicit conversions from a known type to initialize an object
    • assignment operator (assignment operator): Allows an implicit conversion from a known type to be used to assign a value
    • type conversion operator (Type-cast opertor, PS: Also called operator type): Allows to go to a known type

For example:

Implicit conversion of classes: #include <iostream>using namespace Std;class A {};class B {public:  //Conversi On from A (constructor):  B (const a& x) {}  //conversion from A (Assignment):  b& operator= (const A&AM P x) {return *this;}  Conversion to a (type-cast operator)  operator A () {return A ();}; int main () {  A foo;  B bar = foo;    Calls constructor  bar = foo;      Calls assignment  foo = bar;      Calls Type-cast operator  return 0;}

The type conversion operator uses a known statement: With a opertor keyword followed by the target type and an empty parenthesis. Remember: The return value type is the target type, and it is not indicated before the operator keyword. (PS: General operator overloaded member function, return value type is written in front of operator)


Explicit keywords

On a function call, C + + allows an implicit conversion of each parameter. This may be a bit problematic for the class because it is not always expected. For example, let's add the following function to the example shown earlier:

VOID fn (B arg) {}

This function has a parameter of type B, but it can also be called with a parameter of type a:

FN (foo);

This may not be expected. However, in any case, you can be protected by using the explicit keyword on the constructor:

/explicit: #include <iostream>using namespace Std;class A {};class B {public:  explicit B (const a& x) {}
   
    b& operator= (const a& x) {return *this;}  Operator A () {return a ();}}; VOID fn (B x) {}int main () {  A foo;  B Bar (foo);  bar = foo;  foo = bar;  fn (foo);  Not allowed for explicit ctor.  fn (bar);    return 0;}
   

In addition, the constructor for the explicit keyword cannot be invoked using an assignment statement, in which case the bar cannot be constructed like this:

B bar = foo;

Type conversion member functions (previous chapters and descriptions) can also be specified as explicit. This also protects the conversion of the target type as if it were protected by the explicit modified ctor.


Type conversions

C + + is a strongly typed language. Most transformations, especially those that use different representations of values (conversions between types), require explicit conversions, which are called type conversions (type-casting) in C + +. There are two common types of statements for type conversions: functional (functional) and C-style (C-like):

Double x = 10.3;int y;y = int (x);    Functional notationy = (int) x;    

For most basic types, functional forms of this universal type conversion are sufficient. However, this form can be arbitrarily applied to the class and pointer to the class, which causes the code to be syntactically correct, but with a run-time error. For example, the following code can compile without errors:

Class Type-casting#include <iostream>using namespace Std;class Dummy {    double i,j;}; class Addition {    int x, y;  Public:    addition (int a, int b) {x=a; y=b;}    int result () {return x+y;}}; int main () {  Dummy D;  addition * PADD;  Padd = (addition*) &d;  cout << Padd->result ();  return 0;}

The program declares a pointer to addition, but is then assigned an unrelated type with an explicit conversion:

Padd = (addition*) &d;
Unrestricted display type conversions allow arbitrary pointers to be converted to other types of pointers, independent of the type they actually point to. The next member function call will result in a run-time error or some unwanted result.


New Transformation Operators

To control the transformation between these classes, we have four specific types of conversion operators: dynamic_cast, reinterpret_cast, static_cast, and const_cast. Their format is that the new type is enclosed in angle brackets (<>), followed by parentheses around the expression to be transformed.

dynamic_cast <new_type> (expression) reinterpret_cast <new_type> (expression) static_cast <new_type > (expression) const_cast <new_type> (expression)

The traditional type conversion expressions are:

(new_type) expressionnew_type (expression)

But each has its own characteristics:

Static_cast

Static_cast can perform pointer conversions between related classes, not only by moving upward (from a derived class pointer to a base-class pointer), but also by transforming downward (from a base-class pointer to a derived-class pointer). The runtime does not check to ensure that the object has been converted to a complete target type. Therefore, it is the programmer's responsibility to ensure the security of the conversion. On the other hand, it does not increase the overhead of type safety checks like dynamic_cast.

Class Base {};class derived:public base {}; Base * a = new base;derived * b = static_cast<derived*> (a);

This will be a legitimate code, although B points to an incomplete object and can cause a run-time error when it is dereferenced.

As a result, the static_cast is not only able to perform implicit transformations (PS: upcast, integer conversions), but also to reverse the transformation (PS: like downcast, but it's best not to do so!). )。

Static_cast is able to perform all of the implicit transformations allowed by transformations (not just between pointers to classes in the example), but also with its reverse transformation (PS: This sentence repeats!). )。 It can:

    • Converts from void* to any type of pointer. In this case, it will ensure that if the void* value can be accommodated by the target pointer value, the result pointer value will be the same
    • converting integers, floating-point values, and enumeration types to enum types

In addition, static_cast can perform the following:

    • Explicitly call a single-argument constructor (Single-argument constructor) or a transformation operator (conversion operator)
    • Convert to rvalue reference (rvalue reference, ps:c++11)
    • Converting enumeration values to integer or floating-point values
    • Converts any type to void, computes and ignores this value

(Ps:static_cast can replace most of the C-style basic type conversions)


Reinterpret_cast

Reinterpret_cast converts any pointer type to any other arbitrary pointer type, even if it is an unrelated class. The result of this operation is a simple binary copy of the value from one pointer to another. All pointer transformations are allowed: both the pointer type and the content of the reference are not checked.

It can also convert a pointer type to an integral type, or from an integral type to a pointer. The format of the pointer represented by this integer value is platform-dependent (platform-specific, PS: May be related to the size segment). The only guarantee is that a pointer conversion to an integral type is sufficient to fully accommodate it (such as intptr_t), guaranteeing the ability to convert back to a valid pointer.

The ability to use reinterpret_cast but not the static_cast transformation is a low-level operation based on the binary representation of the re-interpretation (reinterperting) type, in most cases the result of the code is System-dependent (system-sepcific), And not portable (non-portable). For example:

Class A {/* ... */};class B {/* ... */}; A * a = new A; b * b = reinterpret_cast<b*> (a);

This code compiles without much feeling, but from then on, B points to a completely unrelated and not complete Class B object A, and dereference B is unsafe.

(Ps:reinterpret_cast can replace most C-style pointer conversions)


Const_cast

This type of conversion is used to manipulate the const property of the object that the pointer refers to, including settings and removal. For example, pass a const pointer to a function that expects the Non-cast parameter:

Const_cast#include <iostream>using namespace Std;void print (char * str) {  cout << str << ' \ n ';} int main () {  const char * c = "sample text";  Print (Const_cast<char *> (c));  return 0;}
Program output: Sampel text

This example is guaranteed to work because the function simply prints and does not modify the object it refers to. Also remember: removing the const attribute of the object being referred to, and then modifying it will result in undefined behavior (undefined behavior, PS: The standard has no defined behavior).

dynamic_cast

Dynamic_cast can only be used for pointers and references to classes (or void*). Its purpose is to ensure that the outcome of the transformation points to the available complete objects of a target type.

This naturally includes the upward transformation of the pointer (pointer upcast, which shifts from the derived class pointer to the base class pointer), and the same method allows implicit conversions.

But dynamic_cast can also be transformed downward (downcast, from a base-class pointer to a derived-class pointer) to a polymorphic class (with virtual members), when and only if the object is a legitimate, complete target type Object (transformation will succeed). For example:

/dynamic_cast#include <iostream> #include <exception>using namespace Std;class Base {virtual void dummy () {} };class derived:public Base {int A;}; int main () {  try {    Base * PBA = new Derived;    Base * PBB = new Base;    Derived * PD;    PD = Dynamic_cast<derived*> (PBA);    if (pd==0) cout << "Null pointer on first type-cast.\n";    PD = Dynamic_cast<derived*> (PBB);    if (pd==0) cout << "Null pointer on second type-cast.\n";  } catch (exception& e) {cout << "exception:" << E.what ();}  return 0;}
Program output: Null pointer on second type-cast.
Versatility Tips: This type of dynamic_cast requires run-time type recognition(run-time type information) to keep track of the dynamic type. Some compilers support this feature, but they are turned off by default. At this point, you need to turn on run-time type checking to make the dynamic_cast run well.

The next code, trying to do two times from the base* type pointer to the derived* type of pointer transformation, but only for the first time is successful. See their respective initializations:

Base * PBA = new Derived; Base * PBB = new Base;

Although two pointers are pointers of type base*, PBA actually points to the type of object that is derived, and PBB points to an object of base type. Therefore, when they are dynamic_cast for type conversion, PBA points to an object of the complete derived class, and PBB points to a base type object, not a full derived type.

When the dynamic_cast (in-expression) pointer is not a complete desired object type (the second transformation in the previous example), it will get a null pointer (null pointer) to indicate a transformation failure (PS: This is the uniqueness of dynamic_cast). If dynamic_cast is used to convert a reference type, and the conversion is not possible, the corresponding Bad_cast exception will be thrown.

Dynamic_cast also allows other implicit conversions of pointers: convert null pointers between pointer types (even unrelated classes), and convert arbitrary pointer types to void* pointers. (PS: Not very common)

(Ps:dynamic_cast rtti Ability is c-like can not do, its downcast effect is equivalent to Java isinstanceof)


typeID

typeID allows you to check the type of an expression:

typeID (expression)

This operator returns a reference to a constant object (constant object) that defines the Type_info type in the standard header file <typeinfo>. The value returned by a typeid can be compared with the value returned by another typeid with the = = operator and the! = operator, or you can get a string by using its name () member (null-terminated character Sequence) represents his data type or class name.

Typeid#include <iostream> #include <typeinfo>using namespace Std;int main () {  int * A, A, b;  a=0; b=0;  if (typeID (a)! = typeID (b))  {    cout << "A and b are of different types:\n";    cout << "A is:" << typeid (a). Name () << ' \ n ';    cout << "B is:" << typeid (b). Name () << ' \ n ';  }  return 0;}
Program output:

A and B are of different types:a is:int *b is:int  

When typeID is used on a class, typeID uses Rtti to track the dynamic type Object (PS: virtual function). When typeID is used for an expression of a polymorphic class type, the result will be the full derived object:

typeID, polymorphic class#include <iostream> #include <typeinfo> #include <exception>using Namespace Std;class Base {virtual void F () {}};class derived:public base {};int main () {  try {    base* a = new B ASE;    base* B = new Derived;    cout << "A is:" << typeid (a). Name () << ' \ n ';    cout << "B is:" << typeid (b). Name () << ' \ n ';    cout << "*a is:" << typeid (*a). Name () << ' \ n ';    cout << "*b is:" << typeid (*b). Name () << ' \ n ';  } catch (exception& e) {cout << "exception:" << e.what () << ' \ n ';}  return 0;}
Program output:

A is:class base *b is:class base **a is:class base*b Is:class Derived

Remember: The string returned by the name member of Type_info is dependent on your compiler and library implementations. is not necessarily a typical class name, for example, the compiler will generate its own output (PS:GCC will not generate such a nice-looking type name).

Remember that the type typeid is used for the pointer is the type of the pointer (a, type B is base*). However, when typeid are used on objects (such as *a and *b), typeid produce their dynamic types (for example, the complete object that they eventually derive from).

If the type of typeid evaluation is a pointer to the computed value of the dereference, and the pointer points to a null value, typeID throws a Bad_typeid exception. (PS: This shows that the use of dynamic_cast and typeID code, need to consider the problem of exception security)


Postscript

Some C + + conceptual nouns are not very clear, they give the original text, cross-reference, the reader's own discretion. In addition, I adjusted the dynamic_cast in the text of the order, the reason is two. First, the role of static_cast and Interpret_cast and c-like transformation should be similar to the previous article. The second is that dynamic_cast and typeid are related to Rtti and should be approached.

If you feel that I have a problem in translation can leave a message or mail exchange.

C + + type conversions

Related Article

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.