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 usedUsage: 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. |