Common Problems With downcast using dynamic_cast in C ++ [rtti]

Source: Internet
Author: User
The type conversion under polymorphism is used for a question in the exercise class for freshman students. For the type conversion of multi-state inheritance, there are two types of conversion: upcast and downcast. The difference between the two types is easy to see from the name. The conversion from a derived class to a base class is upcast. The conversion from a base class to a derived class is called downcast. Of course, this base class should contain virtual functions, that is, the base class is an abstract class. Because dynamic_cast is mainly used for multi-state type conversion.
The following is an example in deitel:
Int main ()
{
// Set floating-point output formatting
Cout <fixed <setprecision (2 );

// Create vector employees
Vector <employee *> employees (4 );

// Initialize vector with employees
Employees [0] = new salariedemployee ("John", "Smith ",
"111-11-1111", 800.00 );
Employees [1] = new commissionemployee ("Sue", "Jones ",
"222-22-2222", 10000,. 06 );
Employees [2] = new basepluscommissionemployee ("Bob ",
"Lewis", "333-33-3333", 300,500 0,. 04 );
Employees [3] = new hourlyemployee ("Karen", "price ",
"444-44-4444", 16.75, 40 );

// Generically process each element in vector employees
For (INT I = 0; I <employees. Size (); I ++ ){

// Output employee information
Employees [I]-> Print ();

// Downcast pointer
Basepluscommissionemployee * commissionptr = dynamic_cast <basepluscommissionemployee *> (employees [I]);

// Determine whether element points to base-salaried
// Commission employee
If (commissionptr! = 0 ){
Cout <"old base salary: $" <commissionptr-> getbasesalary () <Endl;
Commissionptr-> setbasesalary (1.10 * commissionptr-> getbasesalary ());
Cout <"new base salary with 10% increase is: $" <commissionptr-> getbasesalary () <Endl;

} // End if

Cout <"earned $" <employees [I]-> earnings () <Endl;

} // End

// Release memory held by vector employees
For (Int J = 0; j <employees. Size (); j ++ ){

// Output class name
Cout <"\ ndeleting object"
<Typeid (* employees [J]). Name ();

Delete [] employees;

} // End

Cout <Endl;

Return 0;

} // End main

The red statement indicates that dynamic_cast is used to convert the employee (abstract base class) to the basepluscommissionemployee type.

Everything else in the program is OK, but there may be warning: 'dynamic _ cast' used on Polymorphic type 'class employee' with/GR-; unpredictable behavior may result; 'typeid' used on Polymorphic type 'class employee' with/GR-; unpredictable behavior may result.
It turns out that the support for dynamic dynamic_cast typeid is not enabled by default.
Open rtti in menu-> Project-> setting-> C/C ++-> C ++ program under VC ++ 6.0. The operation is successful. ----------------------------------------------- OK.

# Include <stdio. h>
# Include <iostream>
# Include <tchar. h>
Using namespace STD;

Class {
Public:
Virtual int F () {return 0 ;};
PRIVATE:
Int B;
};

Class B: Public {
Public:
Int F () {return 0 ;};
PRIVATE:
Int C;
};

Int _ tmain (INT argc, tchar * argv [])
{
A * A = new;
B * B = new B;
B = dynamic_cast <B *> ();
Return 0;
}

 

You can debug B = NULL;

That is to say, it is safe to use dynamic_cast during download, but its pointer actually points to null. Next we will repost and compare various types of conversion functions.

2.Explicit type conversion (forced conversion): Legacy forced type conversion is not recommended.
Possible causes of forced type conversion:
1) overwrite the standard conversion (implicit)
2) There may be multiple conversions and a specific conversion is required.
3) There is no implicit type conversion. The type conversion is required for a single user.

The following are four common display conversion methods,From strong to weak should be: reinterpret_cast, legacy conversion, static_cast, dynamic_cast.

(1) static_cast
Usage: static_cast <type-ID> (exdivssion)
This operator converts exdivssion to type-id,However, there is no runtime type check to ensure the security of the conversion.. It has the following usage:
① It is used to convert pointers or references between classes and subclasses in the class hierarchy.
A. It is safe to perform upstream conversion (converting the pointer or reference of a subclass to a base class;
B. 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_cast cannot convert the const, volitale, or _ unaligned attributes of exdivssion.

(2) dynamic_cast
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.
Note: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;
Dynamic_cast provides the type check function for downstream conversions, which is safer than static_cast.
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_cast does not have this restriction.
This is because the runtime type check requires the runtime type information, which is stored in the virtual function table of the class (the concept of the virtual function table can be seen in detail, 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, using static_cast for conversion is not allowed, and errors will occur during compilation. Using dynamic_cast for conversion is allowed, and the result is a null pointer.

(3) reinterpret_cast
Usage: reinterpret_cast (exdivssion)
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.

(4) const_cast
Usage: const_cast (exdivssion)
This operator is used to modify the const or volatile attributes of the type. Apart from the const or volatile modifier, The type_id and exdivssion types are the same.
Constant pointers are converted to non-constant pointers and still point to the original object;
Constant reference is converted to a non-constant reference and still points to the original object. Constant object is converted to a non-constant object.

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 (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;
Using 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.
Example:
========================================================== =====
= Dynamic_cast. vs. static_cast
========================================================== =====
Class B {...};
Class D: Public B {...};
Void F (B * pb)
{
D * pd1 = dynamic_cast (PB );
D * Pd2 = static_cast (PB );
}
Dynamic_cast can be used to perform a downward transformation in the inheritance system. It converts a base class pointer to a derived class pointer, Which is stricter and more secure than static_cast. Dynamic_cast is less efficient than static_cast in execution, but static_cast can complete ing in a wider range. This unrestricted ing is always insecure. In addition to static navigation at the class level, the transformation types covered by static_cast also include non- ing transformations and narrow transformations (such transformations can cause object slicing and information loss), forced change with void *, implicit type change, etc...

========================================================== =====
= Static_cast. vs. reinterdivt_cast
========================================================== =====

Reinterpret_cast is used to map to a completely different type. This keyword is used when we need to map the type back to the original type. The type we map to is only for xuanjicang and other purposes, which is the most dangerous of all mappings. (This sentence is the original statement in C ++ programming ideas)
The static_cast and reinterdivt_cast operators modify the operand type. They are not reciprocal. static_cast performs conversions using type information during compilation, and performs necessary checks during conversions (such as cross-border pointer calculation and type check). Its operations are relatively safe. On the other hand, reinterdivt_cast only reinterprets the BIT model of the given object without binary conversion. The example is as follows:
Int n = 9; double D = static_cast <double> (N );
In the above example, we convert a variable from int to double. These types of binary expressions are different. To convert an integer 9 to a double-precision integer 9, static_cast needs to complement the bit with a double-precision integer d correctly. The result is 9.0. The reinterpret_cast behavior is different:
Int n = 9;
Double D = reinterpret_cast (N );
This time, the results are different. After calculation, D contains useless values. This is because reinterpret_cast only copies N bits to D and does not perform necessary analysis.
Therefore, use reinterpret_cast with caution.

========================================================== ======================================
1. dynamic_cast <type-ID> (expression)
Convert expression to a pointer with type-id. Type-ID must be a pointer, reference (a defined class), or void pointer. If it is a pointer, expression must also be a pointer or reference.

A. If type-ID is the direct or indirect base class pointer of expression, the result will be the type-ID type pointer to the expression object. This is called "upcast ". For example:
Class B {...};
Class C: Public B {...};
Class D: Public c {...};

Void F (D * PD)
{
C * Pc = dynamic_cast <C *> (PD); // OK
B * pb = dynamic_cast <B *> (PD); // OK
}

B. If type-ID is void *, the actual type of expression will be checked during runtime. The result is a pointer to the complete object of expression. For example:
Class {...};
Class B {...};

Void F ()
{
A * pA = new;
B * pb = new B;
Void * Pv = dynamic_cast <void *> (PA );
// PV points to an object.
...
Pv = dynamic_cast <void *> (PB );
// PV points to an object of B
}

C. If type-ID is not void *, the system checks whether the expression object can be transformed to type-ID at runtime.
C-1. If expression is a base class of type-ID, it will check whether expression points to a complete object of type-ID at runtime. If yes, the result is a pointer to the object. Otherwise, an error occurs. For example:
Class B {...};
Class D: Public B {...};
Void F ()
{
B * pb = new D;
B * PBS = new B;

D * Pd = dynamic_cast <D *> (PB); // OK.
D * Pd2 = dynamic_cast <D *> (PBS) // error.
}
This is called "downcast"
C-2. For multi-inheritance, such:
Class {...};
Class B: Public A {...}; // B inherits from
Class C: Public A {...}; // C inherits from
Class D: Public B, public c {...}; // D inherited from B, c
At this time, the pointer to D can safely cast B or C (see above). But what if I cast it to? Is that true?
D * Pd = new D;
A * pA = dynamic_cast <A *> (PD); // error. I don't know which.
In this case, we can first transform to B (or C), and then indirectly batch to. As follows:
B * pb = dynamic_cast <B *> (PD );
A * pA = dynamic_cast <A *> (PB); // OK

C-3. Situations of virtual inheritance
Class A {...} // you can simply write the class name later.
Class B: vitual public;
Class C: Public B;
Class D: Public B;
Class E: publc C, public d;
If the object of E or the sub-object of a wants to transform to B, it will be a failure (for the reason, see). In this case, you need to first transform to a complete e object, then, a clear transformation operation is performed layer by layer.

C-4.
Class;
Class B: Public;
Class C: Public;
Class D;
Class E: Public B, public C, publc D;

Assume that a pointer to an object of E and A sub-object of D needs three transformations to obtain the sub-object of a from the sub-object of D.
First, the pointer of the D type is transformed into a pointer of the E type, and then transformed to a layer by layer. For example
Void F (D * PD)
{
E * Pe = dynamic_cast <E *> (PD );
B * pb = dynamic_cast <B *> (PE );
// Or B * pb = PE;
A * pA = dynamic_cast <A *> (PB );
// Or a * pA = Pb;
}

C-5. (Cross) Cross conversion (Cross cast ).
In the preceding example, we transform from B (or subclass) to D (or subclass.

Ii. static_cast
Static_cast can be used for static navigation at the class level, without ing transformation or narrow Transformation (information loss). The static_cast application is wider, but as mentioned earlier, we should use the former in class hierarchy navigation transformation, because the latter static_cast may mean taking risks (for example, losing information like forcing conversion ). However, if there is no virtual function in a class hierarchy or we are sure that there is information that he/she allows us to safely map down, the latter static_cast is faster.

It converts the expression type to the Type-ID type. It can be a transformation of a class (including inheritance) or a transformation of a common type (such as int-> float ). Note that it does not perform type check during running, so it may be insecure. For example, convert a base class to a pointer of a derived class.

3. const_cast
To put it simply, the function is to remove the const, volatile, and _ unaligned attributes of a class.
Iv. reinterpret_cast
In C ++, reinterpret_cast converts data from one type to another. The so-called "usually provides lower-level re-interpretation for the bit mode of the operand" means re-interpretation of the data in the form of binary existence. For example:
Int I;
Char * P = "this is a example .";
I = reinterpret_cast <int> (P );
In this case, the values of I and P are exactly the same. Reinterpret_cast is used to interpret the value of P as an integer in binary (bit mode) mode and assign it to I. An obvious phenomenon is that there is no digital loss before and after conversion.
========================================================== ======================================
Static_cast: cast type conversion. Therefore, it can certainly be used for cast between classes with inheritance relationships. There are three types of subdivisions:
Upcast: Just same as dynamic_cast. Because the runtime type check is not required, the efficiency is higher than that of dynamic_cast;
Downcast: Not safe. It is not recommended.
Crosscast: unavailable. Compilation error.

Dynamic_cast: Cast between classes with inheritance relationships. It is very safe, but it requires a runtime type check and virtual table support, which is less efficient. Category 3:
Upcast: OK. Static_cast is enough.
Downcast: it is required. In this way, the NULL pointer can be obtained only when downcast is incorrect, instead of a non-null pointer that can be used as static_cast.
Crosscast: it can only be used.
========================================================== ======================================
In most cases, you only need to know that you are used to writing these operators as follows:
(Type) Expression
Now you should always write like this:
Static_cast (expression)

For example, if you want to convert an int to a double type, the expression containing the int type variable will generate a floating point value. If you use C-style type conversion, you can write as follows:
Int firstnumber, secondnumber;
...
Double result = (double) firstnumber)/secondnumber;
If you use the new type conversion method, you should write as follows:
Double result = static_cast <double> (firstnumber)/secondnumber;

Const_cast is usually used to remove the constant of an object (cast away the constness ). It is the only C ++-style transformation operator with this capability. 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 object;
Constant reference is converted to a non-constant reference and still points to the original object;
Constant objects are converted to very large objects.
Voiatile and const classes. Take the following example:
Classb {
Public:
Int m_inum;
}
Void Foo (){
Const bb1;
B1.m _ inum = 100; // an error is reported during compilation because B1 is a constant object and cannot be changed;
B b2 = const_cast <B> (B1 );
B2.m _ inum = 200; // fine
}
Using 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.

Dynamic_cast is used to perform security downward or cross-system transformation actions in the inheritance system. That is to say, you can use it to convert a pointer or reference to a base class object to a pointer or reference to a derived class object, and determine whether the transformation is successful. If the transformation fails, it will be expressed as a null pointer (when the transformation object is a pointer) or an exception (when the transformation object is a reference. Dynamic_cast is the only transformation action that cannot be executed by the old syntax. It is also the only transformation action that may consume significant operation costs.
Static_cast has the same power and significance as the old-style C transformation, as well as the same restrictions. For example, convert a non-const object to a const object, or convert 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-to-derived. However, he cannot convert the const to non-Const. Only the const-cast can do this.
Dynamic_cast has the type check function, which is safer than static_cast.
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 <D *> (PB );
D * Pd2 = dynamic_cast <D *> (PB );
}
In the code snippet above, if PB points to a D-type object, pd1 and Pd2 are the same, and it is safe to execute any operations of the D-type on these two pointers; however, if Pb points to a B-type object, pd1 will be a pointer to this object, and operations of Type D on it will be insecure (for example, accessing m_szname ), pd2 is a null pointer.
Note: B 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_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 <D *> (PB); // compile Error
D * Pd2 = dynamic_cast <D *> (PB); // Pd2 is null
Delete Pb;
}
In function Foo, using static_cast for conversion is not allowed, and errors will occur during compilation. Using dynamic_cast for conversion is allowed, and the result is a null pointer.
Reinterpret_cast intends to perform 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. This type of transformation is rare outside of low-level code.
The old type of transformation is still valid in C ++, but it is recommended to use the new form here. First, they are easier to identify in code (not only for humans, but also for tools like grep), thus simplifying the process of "identifying where type systems are destroyed. Second, the narrower the goal of each transformation action, the more likely the compiler will diagnose the wrong application. For example, if you want to remove constants, you cannot compile them unless you use const_cast in the new transformation.

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.