In C++, there are four specific type conversion mechanisms, namely:
-
static_cast (static conversion, executed at compile time)
static_cast
can perform type conversion on objects, but this conversion is determined at compile time. It applies to the following situations:- Pointer type from pointer type to base type.
- From a pointer type of a base type to a pointer type of a derived class (but without runtime type checking, potentially unsafe).
- Between numeric types (such as
int
tofloat
,float
toint
, etc.). - From enumeration types to integers or floating point numbers.
- From integers or floating point numbers to enumeration types.
However, for conversions involving objects within a class hierarchy, especially when polymorphism is involved, it is generally safer to usedynamic_cast
(this requires virtual function support to check at runtime type).
However, if you are sure that the conversion is legal and safe, you can usestatic_cast
to convert between class objects, but this usually requires a deep understanding of the structure of the code.
- Purpose: This is the most commonly used conversion method, used for conversion between various common types, such as integer to floating point number, floating point number to integer, etc.
- Example:
float f = 3.14; int i = static_cast<int>(f);
- Note:
static_cast
cannot convert all CV qualifiers (const and volatile) of the type.
-
dynamic_cast
- Purpose: Mainly used for conversion between polymorphic types. When performing a downcast (from a base class to a derived class),
dynamic_cast
checks at runtime whether the conversion is legal. If the conversion is illegal, for pointer types, it will returnnullptr
; for reference types, it will throw abad_cast
exception.
dynamic_cast
is mainly used to handle the conversion of objects in a class hierarchy with virtual functions. It is mainly used in the following situations:
- Pointer conversion:
- From base class pointer type to derived class pointer type.
- From derived class pointer type to base class pointer type.
Note: The base class here should usually contain at least one virtual function.
- Quote conversion:
- From a base class reference type to a derived class reference type.
- If the cast fails (for example, the actual object is not the expected derived type), a
std::bad_cast
exception is thrown.
- Conversions related to virtual base classes: In cases where virtual inheritance is involved,
dynamic_cast
can be used to perform appropriate conversions within the class hierarchy.
The main advantage ofdynamic_cast
is that it checks the validity of the conversion at runtime. If the conversion is illegal or unsafe, it will returnnullptr
for pointer types; it will throw an exception for reference types.
For example, consider the following scenario, whereDerived
is a derived class ofBase
:
- Purpose: Mainly used for conversion between polymorphic types. When performing a downcast (from a base class to a derived class),
Base* basePtr = new Derived(); Derived* derivedPtr = dynamic_cast<Derived*>(basePtr); if (derivedPtr) {<!-- --> // Conversion successful, derivedPtr can be used safely } else {<!-- --> // Conversion failed }
Here, using `dynamic_cast` ensures that the conversion is safe and allows the runtime to check the validity of the conversion. - example:
Base* bPtr = new Derived(); Derived* dPtr = dynamic_cast<Derived*>(bPtr);
- Note: Using `dynamic_cast` requires a virtual function in the base class so that runtime type information can be checked. 3. **`const_cast (constant conversion, executed at compile time)`** - Purpose: Const or volatile qualifier used to convert types. However, modifying the value after removing `const` through `const_cast` is undefined behavior, unless the memory itself is non-`const`. (Even if the conversion is used, the conversion is only performed at the compiler's rule level, but no changes are sent at other levels (for example, the hardware level uses ro memory. At this time, although the const is converted, it will still be modified when it is modified. An unpredictable error occurred))
const_cast
is mainly used to modify the const
or volatile
qualification of variables. It is one of four C++ casts and is used in the following situations:
-
Remove
const
qualification:- A pointer to a
const
object can be converted to a pointer to a non-const
object. - You can convert a reference to a
const
object into a reference to a non-const
object.
For example:
const int val = 10; int* ptr = const_cast<int*>( & amp;val);
- A pointer to a
-
Remove
volatile
qualification:- A pointer to a
volatile
object can be converted to a pointer to a non-volatile
object. - You can convert a reference to a
volatile
object into a reference to a non-volatile
object.
- A pointer to a
-
Add
const
orvolatile
qualification:- Although this is uncommon,
const_cast
can also be used to convert a pointer or reference to a non-const
object toconst
orvolatile
.
- Although this is uncommon,
It should be noted that although const_cast
can be used to remove the const
qualification of an object, if the object was originally defined in the const
way, Then trying to modify its value is undefined behavior. In other words, const_cast
gives you the ability to change the value of an object, but it doesn’t mean you always have permission to do so.
explain:
const_cast
is used to modify the const
or volatile
attribute of a type. However, using const_cast
to remove the const
qualification and modify its value is dangerous, especially for objects that are originally const
. This operation may result in undefined behavior.
“Undefined Behavior” (UB for short) is a proper noun in C++, which means that when a program exhibits such behavior, the standard does not define specific behavior or results. In short, the program may do anything in this case: it may work normally, it may crash, it may produce incorrect output, etc.
Consider the following example:
const int x = 10; int* px = const_cast<int*>( & amp;x); *px = 20; // Undefined behavior!
Here, we declare a const int
variable x
. Then we use const_cast
to remove its const
ness and try to modify its value. This is undefined behavior.
The reason is that a const
object may be stored in read-only memory (e.g., a text or code segment on some hardware architectures), and trying to modify it may cause the program to crash. Even if it doesn’t crash, since the compiler knows that x
is a constant, it may have done some optimization so that even if we try to change its value, the actual value may not change.
In short, const_cast
is a powerful but dangerous tool. When you decide to use it, make sure you know what you are doing and avoid modifying the value of an object that is originally const
.
- Example:
const int a = 10; int* pa = const_cast<int*>( & amp;a);
- Note:
const_cast
is only used to adjust the type qualifier of an object.
-
reinterpret_cast (reinterpret type conversion)
- Purpose: Used for low-level type conversion, which can convert any type of pointer to any other type of pointer, or any pointer to an integer type, and vice versa. The result is platform dependent and rarely used directly.
reinterpret_cast
is one of four type casts provided by C++ and performs low-level, machine-dependent conversions. Because of how powerful and dangerous it is, extreme caution is required when using it. The following are several situations wherereinterpret_cast
is applicable:
- Purpose: Used for low-level type conversion, which can convert any type of pointer to any other type of pointer, or any pointer to an integer type, and vice versa. The result is platform dependent and rarely used directly.
-
Conversion between pointers and integers:
You can convert a pointer to a sufficiently large integer type, or an integer to a pointer.int* ptr = new int(10); std::uintptr_t intVal = reinterpret_cast<std::uintptr_t>(ptr); int* newPtr = reinterpret_cast<int*>(intVal);
-
Conversion between pointers of different types:
For example, converting from avoid*
pointer to a pointer of another type, or converting a pointer of one type to a completely different type.double value = 3.14; double* dptr = & amp;value; void* vptr = reinterpret_cast<void*>(dptr); double* newDptr = reinterpret_cast<double*>(vptr);
-
Conversion between different types of references:
This can be used to cast a reference of one type to a different type.int x = 10; double & amp; ref = reinterpret_cast<double & amp;>(x);
-
Conversion between pointers to class members:
You can convert a pointer to a data member of one class to a pointer to a data member of another class, or you can convert a pointer to a member function of one class to a pointer to a member function of another class. -
Conversion between pointer and member pointer:
In some cases, it may be necessary to convert between regular pointers and member pointers of a class.
It should be noted that the conversion provided by reinterpret_cast
is a simple byte interpretation by the compiler and does not involve runtime type information or checking. Therefore, while it is very flexible, it is also prone to undefined behavior. Therefore, try to avoid using it unless you know exactly what you are doing.
- Example:
int i = 42; int* p = & amp;i; long address = reinterpret_cast<long>(p);
- Note:
reinterpret_cast
is very dangerous and should be used only when the meaning and consequences of the conversion are clear.
In short, each type conversion operator has its specific application scenarios. When performing type conversion, the most appropriate conversion mechanism should be selected to ensure the correctness and readability of the code.
Summary:
static_cast: used for conversion between floating point and integer
dynamic_cast: used for polymorphic conversion between classes
const_cast: used to convert CV qualifiers (only a rule-based conversion, that is, no error will be reported when modifying the const value after conversion)
reinterpret_cast: used for pointer type conversion, which can convert a pointer to any type of pointer (very dangerous, so pay special attention when converting)
Application scenarios of dynamic_cast conversion:
dynamic_cast
is mainly used to safely perform downward or side casts in the inheritance hierarchy of a class. It checks at runtime whether the cast is legal. If illegal, it will return nullptr
for pointer type conversion, and throw exception std::bad_cast
for reference type conversion.
Application scenario: Imagine a graphics system with a base class Shape
, from which Circle
and Rectangle
are derived. >. Now you have a pointer to a Shape
and want to determine whether it points to a Circle
object so that you can call a Circle
-specific method.
class Shape {<!-- --> public: virtual ~Shape() {<!-- -->} // ... other members ... }; class Circle : public Shape {<!-- --> public: void drawCircle() {<!-- --> // draw a circle } // ... other members ... }; class Rectangle : public Shape {<!-- --> public: void drawRectangle() {<!-- --> // draw a rectangle } // ... other members ... }; void Draw(Shape* shape) {<!-- --> Circle* circle = dynamic_cast<Circle*>(shape); if(circle) {<!-- --> circle->drawCircle(); } else {<!-- --> Rectangle* rect = dynamic_cast<Rectangle*>(shape); if(rect) {<!-- --> rect->drawRectangle(); } } }
In the above code, we can safely check the actual type pointed to by shape
and perform specific operations accordingly.
Note: For dynamic_cast
to work, the source type of the cast must contain virtual functions, making it polymorphic. This is why we have a virtual destructor in the Shape
class.