Static_cast, dynamic_cast, and const_cast

Source: Internet
Author: User
Tags class manager

First, let's review the C ++ type conversion:

C ++ conversions include implicit conversions and Explicit conversions.

Part 2. implicit type conversion

When implicit type conversion occurs

In the following cases, the compiler automatically converts the object type:

  • In most expressions, integer values smaller than int types are first upgraded to larger integer types.
  • In conditions, non-boolean values are converted to boolean values.
  • During initialization, the initial value is converted to the variable type. In the value assignment statement, the right operation object is converted to the type of the Left operation object.
  • If there are multiple types of arithmetic operations or relational operations, you need to convert them to the same type.
  • Type conversion also occurs when the function is called.

 

It is also known as "standard conversion", including the following situations:
1) arithmetic conversion: in a mixed-type arithmetic expression, the widest data type is the target conversion type.

 

Int ival = 3;
Double dval = 3.14159;

Ival + dval; // ival is upgraded to double type

2) one type expression is assigned to another type of object: The target type is the type of the object to be assigned.

Int * Pi = 0; // 0 is converted to int * type
Ival = dval; // double-> int

Exception: When the void pointer is assigned to a pointer of another specified type, no standard conversion exists and compilation error occurs.

3) pass an expression as a real parameter to the function call. At this time, the form parameters and real parameter types are inconsistent: The target conversion type is the type of the form parameter.

Extern double SQRT (double );

Cout <"the square root of 2 is" <SQRT (2) <Endl;
// 2 is upgraded to the double type: 2.0

4) return an expression from a function. The expression type is inconsistent with the return type: the target conversion type is the return type of the function.

Double Difference (INT ival1, int ival2)
{
Return ival1-ival2;
// The return value is elevated to the double type.
}

 

Other implicit Conversions

In addition to arithmetic conversions, there are also several types of implicit conversions, including the following:

Convert an array to a pointer: In most expressions that use arrays, the array is automatically converted to a pointer pointing to the first element of the array:

Int Ia [10]; // an array containing 10 Integers

Int * IP = IA; // IA is converted to a pointer pointing to the first element of the array.

If an array is used as a parameter for the decltype keyword, or as an operation object for the operators such as the Ampersand (&), sizeof, and typeid, the preceding conversion does not occur. Similarly, if an array is initialized using a reference, the above conversion will not happen.

Pointer Conversion: C ++ also specifies several other conversion methods, including the constant integer 0 or the literal nullptr can be converted to any pointer type, and the pointer pointing to any extraordinary amount can be converted to void *; the pointer to any object can be converted to const void *.

Convert to boolean type: There is a mechanism for automatic conversion from arithmetic type or pointer type to boolean type. If the pointer or arithmetic value is 0, the conversion result is false; otherwise, the conversion result is true;

Convert to constant: Allows you to convert a pointer to a constant type to a pointer to the corresponding constant type. This is also true for references. That is to say, if T is a type, we can convert the pointer or reference pointing to t into a pointer or reference pointing to const T. The opposite conversion does not exist because it tries to delete the underlying Const.

Class type definition Conversion: The class type can define the conversion automatically executed by the compiler, but the compiler can only execute one type of conversion at a time.

 

Part 2. Explicit type conversion


It is called "forced type conversion" (CAST)
C style: (Type-ID)
C ++ style:Static_cast,Dynamic_cast,Reinterpret_cast, AndConst_cast ..

 

Many books have discussed the issue of forced type conversion. The most detailed issue is the design and evolution of C ++, the father of C ++. The best solution is not to use a C-style forced type conversion, but to use the standard C ++ type conversion operator static_cast and dynamic_cast. The standard C ++ has four types of delimiters:Static_cast,Dynamic_cast,Reinterpret_cast, AndConst_cast. Next we will introduce them one by one.

Static_cast

Usage:Static_cast<Type-ID> (expression)

Note: This operator converts expression to the Type-ID type, but does not check the runtime type to ensure the conversion security.
Source: Why is mandatory static_cast conversion required?
Case 1: void pointer-> other type pointer
Case 2: Change the standard conversion
Case 3: Avoid ambiguity of Multiple conversions

Usage:Static_cast<Type-ID> (exdivssion)
This operator converts exdivssion to the Type-ID type, but there is no runtime type check to ensure the conversion security. It has the following usage:
① It is used to convert pointers or references between classes and subclasses in the class hierarchy.
It is safe to perform upstream conversion (converting the pointer or reference of a subclass to a base class;
When performing a downstream conversion (converting a base class pointer or reference to a subclass), it is not safe because there is no dynamic type check.
② It is used for conversion between basic data types. For example, convert int to Char and convert int to enum. The security of such conversions must also be ensured by developers.
③ Convert a null pointer to a null pointer of the target type.
④ Convert any type of expression to void type.

Note:Static_castThe const, volitale, or _ unaligned attribute of exdivssion cannot be converted.



It has the following usage:
  • It is used to convert pointers or references between classes and subclasses in the class hierarchy. It is safe to perform upstream conversion (converting the pointer or reference of a subclass to a base class representation). When performing downstream conversion (converting a base class pointer or reference to a subclass pointer or reference, because there is no dynamic type check, it is not safe.
  • It is used to convert between basic data types. For example, convert int to Char and convert int to enum. The security of such conversions must also be ensured by developers.
  • Convert the void pointer to a target pointer (insecure !!)
  • Convert any type of expression to void type.
Note: static_cast cannot convert the const, volitale, or _ unaligned attribute of expression.

Dynamic_cast

Usage:Dynamic_cast<Type-ID> (expression)
Note: This operator converts an expression to an object of the Type-ID type. Type-ID must be a class pointer, class reference, or void *. If type-ID is a class pointer type, expression must also be a pointer, if type-ID is a reference, expression must also be a reference.
Source: Why does dynamic_cast need to be forcibly converted?
Simply put, when virtual functions cannot be used

Usage: dynamic_cast <type-ID> (exdivssion)
This operator converts exdivssion to an object of the Type-ID type. Type-ID must be a class pointer, class reference, or void *;
If type-ID is a class pointer type, exdivssion must also be a pointer. If type-ID is a reference, exdivssion must also be a reference.

Dynamic_cast is mainly used for upstream and downstream conversions between classes, and can also be used for cross conversions between classes.
Dynamic_cast andStatic_castThe results are the same;
When performing a downstream conversion, dynamic_cast has the type check function.Static_castMore secure.

 

Up-casting and down-Casting)

 

When you see this, the reader may ask, which conversions are not secure? According to the previous example, we can see that the security of data comes from two aspects: one is the narrow type conversion, which will lead to the loss of data digits; the other is in the class inheritance chain, forcibly convert the address (pointer) of the parent class object to the address (pointer) of the Child class. This is the so-called downlink conversion. "Bottom" indicates that the inheritance chain goes down (to the subclass direction ).

Similarly, the "up" of the upstream conversion means to go up along the inheritance chain (to the parent class ).

We conclude that the uplink conversion is generally safe, and the downlink conversion is probably insecure.

Why? Because the subclass contains the parent class, the uplink conversion (only the method of the parent class can be called to reference the member variables of the parent class) is generally safe. However, the parent class does not have any information about the subclass. the downlink conversion calls the subclass method and references the member variables of the subclass. None of these parent classes exist, therefore, it is easy to refer to the deer as a horse or point to a memory space that does not exist.

It is worth noting that unsafe conversions may not necessarily lead to program errors. For example, some narrow conversions are frequently used in many occasions, provided that the programmer is careful enough to prevent data overflow; the key to downlink conversion is what its "nature" is. For example, if a parent class Pointer Points to a subclass and then converts the parent class pointer to a subclass pointer, this type of downlink conversion will not be problematic.

 

For Class pointers, C ++ has designed more detailed conversion methods, including:

Static_cast <new_type> (expression)
Dynamic_cast <new_type> (expression)
Reinterpret_cast <new_type> (expression)
Const_cast <new_type> (expression)

This improves the conversion security.


Class B {
Public:
Int m_inum;
Virtual void Foo ();
};

Class D: Public B {
Public:
Char * m_szname [100];
};

Void func (B * pb ){
D * pd1 =Static_cast(PB );
D * Pd2 = dynamic_cast (PB );
}

In the above Code segment, if PB points to a D-type object, pd1 and Pd2 are the same, and it is safe to execute D-type operations on these two pointers;
However, if PB points to a B-type object, pd1 will be a pointer to this object, it is not safe to perform operations of the D type (for example, to access m_szname ),
Pd2 is a null pointer.

Note: B must have virtual functions; otherwise, compilation errors may occur;Static_castThere is no such restriction.
This is because the runtime type check requires runtime type information, which is stored in the virtual function table of the class (
For more information about the concept of virtual function tables, see). Only classes that define virtual functions have virtual function tables,
Classes that do not define virtual functions do not have virtual function tables.

In addition, dynamic_cast also supports cross cast ). The following code is used.
Class {
Public:
Int m_inum;
Virtual void F (){}
};

Class B: Public {
};

Class D: Public {
};

Void Foo (){
B * pb = new B;
Pb-> m_inum = 100;

D * pd1 =Static_cast(PB); // compile Error
D * Pd2 = dynamic_cast (PB); // Pd2 is null
Delete Pb;
}

In function Foo, useStatic_castConversion is not allowed, and errors will occur during compilation. The conversion using dynamic_cast is allowed, and the result is a null pointer.

 


Typical cases:
Wicrosoft provides us with a class library, which provides a class of employee. It is distributed to users by header files eemployee. h and class library. Lib.
Obviously, we cannot obtain the source code of the class implementation.
// Emplyee. h
Class employee
{
Public:
Virtual int salary ();
};

Class MANAGER: Public Employee
{
Public:
Int salary ();
};

Class programmer: Public Employee
{
Public:
Int salary ();
};
Our company established the following classes during development:
Class mycompany
{
Public:
Void payroll (employee * PE );
//
};

Void mycompany: payroll (employee * PE)
{
// Do something
}
However, after the development, we hope to add a bonus () member function to the class hierarchy provided by W $.
Assuming that we know the source code, it is very simple to add a virtual function:
// Emplyee. h
Class employee
{
Public:
Virtual int salary ();
Virtual int bonus ();
};

Class MANAGER: Public Employee
{
Public:
Int salary ();
};

Class programmer: Public Employee
{
Public:
Int salary ();
Int bonus ();
};

// Emplyee. cpp

Int programmer: bonus ()
{
//
}
Payroll () calls bonus () through Polymorphism ()
Class mycompany
{
Public:
Void payroll (employee * PE );
//
};

Void mycompany: payroll (employee * PE)
{
// Do something
// Pe-> bonus ();
}
But now we cannot modify the source code. What should we do? Dynamic_cast debuted!
Add the bonus () Declaration in employee. H, define this function in another place, modify the call function payroll (). recompile, OK
// Emplyee. h
Class employee
{
Public:
Virtual int salary ();
};

Class MANAGER: Public Employee
{
Public:
Int salary ();
};

Class programmer: Public Employee
{
Public:
Int salary ();
Int bonus (); // extend it directly here
};

// Somewhere. cpp

Int programmer: bonus ()
{
// Define
}

Class mycompany
{
Public:
Void payroll (employee * PE );
//
};

Void mycompany: payroll (employee * PE)
{
Programmer * PM = dynamic_cast <programmer *> (PE );

// If PE actually points to a programmer object, dynamic_cast succeeds and starts to point to the start position of the programmer object
If (pm)
{
// Call programmer: bonus ()
}
// If PE does not actually point to the programmer object, dynamic_cast fails and PM = 0
Else
{
// Use employee member functions
}
}


Dynamic_cast is mainly used for upstream and downstream conversions between classes, and can also be used for cross conversions between classes.

When performing upstream conversion between classes, dynamic_cast and static_cast have the same effect. During downstream conversion, dynamic_cast has the type check function, which is safer than static_cast.
Class base
{
Public:
Int m_inum;
Virtual void Foo ();
};

Class derived: public Base
{
Public:
Char * m_szname [100];
};

Void func (base * pb)
{
Derived * pd1 = static_cast <derived *> (PB );

Derived * Pd2 = dynamic_cast <derived *> (PB );
}
In the above Code segment,
If Pb actually points to an object of the derived type, pd1 and Pd2 are the same, and it is safe to execute any operation of the derived type on these two pointers;
If Pb actually points to a base object, pd1 will be a pointer to this object, the derived type operation on it will be insecure (for example, accessing m_szname), and Pd2 will be a null pointer (that is, 0, because dynamic_cast fails ).
Note: The base must have virtual functions; otherwise, compilation errors may occur. static_cast does not have this restriction. This is because the runtime type check requires runtime type information, which is stored in the virtual function table of the class (the concept of virtual function table, in <inside C ++ object model>), only the classes that define virtual functions have virtual function tables. classes that do not define virtual functions do not have virtual function tables.

In addition,Dynamic_castCross cast is also supported ). The following code is used.
Class base
{
Public:
Int m_inum;
Virtual void F (){}
};



Class derived1: public Base
{

};

Class derived2: public Base
{

};

Void Foo ()
{
Derived1 * pd1 = new drived1;

Pd1-> m_inum = 100;

Derived2 * Pd2 = static_cast <derived2 *> (pd1); // compile Error

Derived2 * Pd2 = dynamic_cast <derived2 *> (pd1); // Pd2 is null

Delete pd1;
}
In function Foo, useStatic_castConversion is not allowed, and errors will occur during compilation.Dynamic_castIs allowed, and the result is a null pointer.

Reinpreter_cast

Usage:Reinpreter_cast<Type-ID> (expression)

Description: type-ID must be a pointer, reference, arithmetic type, function pointer, or member pointer. It can convert a pointer to an integer, or an integer to a pointer (first, convert a pointer to an integer, and then convert the integer to the original type of pointer, you can also get the original pointer value ).

This operator is used in many ways.

Const_cast
Usage:Const_cast<Type_id> (expression)
Note: This operator is used to modify the const or volatile attributes of the type. In addition to const or volatile modification, type_id and expression are of the same type.

Constant pointers are converted to non-constant pointers and still point to the original objects. Constant references are converted to non-constant references and still point to the original objects; constant objects are converted to very large objects.

Voiatile and const classes. Take the following example:
Class B {

Public:

Int m_inum;

}

Void Foo (){

Const B B1;

B1.m _ inum = 100; // comile Error

B b2 = const_cast <B> (B1 );

B2. m_inum = 200; // fine
}
The above code will report an error during compilation, because B1 is a constant object and cannot be changed. Use const_cast to convert it into a constant object, you can change its data members at will. Note: B1 and B2 are two different objects.

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.