C + +: four types of cast

1, const_cast

1. The constant pointer is converted into a non constant pointer and still points to the original object;
2. Constant references are converted to non constant references and still point to the original object;
3,const_cast is generally used to modify pointers. Such as const char *p.

#include<iostream>

int main() {
    // Original array
    int ary[4] = { 1,2,3,4 };

    // print data
    for (int i = 0; i < 4; i++)
        std::cout << ary[i] << "\t";
    std::cout << std::endl;

    // Constant array pointer
    const int*c_ptr = ary;
    //c_ptr[1] = 233;   //error

    // By const_ Cast < ty > de constant
    int *ptr = const_cast<int*>(c_ptr);

    // Modify data
    for (int i = 0; i < 4; i++)
        ptr[i] += 1;    //pass

    // Print modified data
    for (int i = 0; i < 4; i++)
        std::cout << ary[i] << "\t";
    std::cout << std::endl;

    return 0;
}

out print:

1   2   3   4
2   3   4   5

Note: const is used for parameters defined as constants in_ Cast may have different effects Similar codes are as follows

#include<iostream>
int main() {
    const int c_val = 233;  //Declared as a constant type
    int &use_val = const_cast<int&>(c_val); //Use de const reference
    int *ptr_val = const_cast<int*>(&c_val);//Use dest pointer

    use_val = 666;  //Undefined behavior
    std::cout << c_val << "\t" << use_val << "\t" << *ptr_val << std::endl;
    *ptr_val = 110; //Undefined behavior
    std::cout << c_val << "\t" << use_val << "\t" << *ptr_val << std::endl;
    return 0;
}

In 2017, the output is:

233 666 666
233 110 110

Undefined behavior: the C + + standard does not specify such behavior The same code will have different effects when using different compilers In vs2017, although C in the code_ val , use_ val , ptr_ The address Val sees is the same But C_ The value of Val has not changed It is possible that after the implementation of some compiler, the C of this code_ Val will be changed It is also possible for the compiler to make a direct error or warning

2, static_cast

static_ The effect of cast is basically the same as that of C language style coercion. Since there is no runtime type check to ensure the safety of conversion, this type of coercion and C language style coercion have potential safety hazards.
Used to convert pointers or references between the base class (parent class) and the derived class (child class) in the class hierarchy. Note: it is safe to perform uplink conversion (convert the pointer or reference of the derived class to the representation of the base class); When performing a downstream conversion (converting a base class pointer or reference to a derived class representation), it is unsafe because there is no dynamic type checking.
Used for conversion between basic data types, such as converting int to char and int to enum. The security of this transformation needs to be maintained by developers.
static_cast cannot convert const, volatile, or__ unaligned attribute. (the first two can be removed by const_cast)
In c++ primer, it is said that any implicit conversion in c + + uses static_cast.

/* Conventional usage */
float f_pi=3.141592f
int   i_pi=static_cast<int>(f_pi); /// i_ The value of PI is 3

/* class Uplink and downlink conversion of */
class Base{
    // something
};
class Sub:public Base{
    // something
}

//  Uplink sub - > base
//Compiled, safe
Sub sub;
Base *base_ptr = static_cast<Base*>(&sub);  

//  Downlink base - > sub
//Compilation passed, unsafe
Base base;
Sub *sub_ptr = static_cast<Sub*>(&base);    

3, dynamic_cast

dynamic_cast casting should be the most special of the four, because it involves object-oriented polymorphism and the state of the program running time, and is also related to the attribute setting of the compiler Therefore, we can't completely use the C language to replace the coercion. It is also the most useful and indispensable coercion

#include<iostream>
using namespace std;

class Base{
public:
    Base() {}
    ~Base() {}
    void print() {
        std::cout << "I'm Base" << endl;
    }
    virtual void i_am_virtual_foo() {}
};

class Sub: public Base{
public:
    Sub() {}
    ~Sub() {}
    void print() {
        std::cout << "I'm Sub" << endl;
    }
    virtual void i_am_virtual_foo() {}
};
int main() {
    cout << "Sub->Base" << endl;
    Sub * sub = new Sub();
    sub->print();
    Base* sub2base = dynamic_cast<Base*>(sub);
    if (sub2base != nullptr) {
        sub2base->print();
    }
    cout << "<sub->base> sub2base val is: " << sub2base << endl;


    cout << endl << "Base->Sub" << endl;
    Base *base = new Base();
    base->print();
    Sub  *base2sub = dynamic_cast<Sub*>(base);
    if (base2sub != nullptr) {
        base2sub->print();
    }
    cout <<"<base->sub> base2sub val is: "<< base2sub << endl;

    delete sub;
    delete base;
    return 0;
}

vs2017 output is:

Sub->Base
I'm Sub
I'm Base
<sub->base> sub2base val is: 00B9E080   // Note: this address is assigned by the system and may not be the same every time

Base->Sub
I'm Base
<base->sub> base2sub val is: 00000000   // The C + + compiler of VS2017 assigns nullptr to the conversion of such errors

As can be seen from the code and output results above:
For pointer conversion from subclass to base class, dynamic_cast was successfully converted without any abnormal operation and achieved the expected results
The conversion from base class to subclass is dynamic_cast does not report an error during conversion, but the output to base2sub is a nullptr, indicating dynamic_cast checks the type conversion and Runtime type information (RTTI) when the program is running
This check mainly comes from virtual function. In the object-oriented idea of C + +, virtual function plays a key role. When a class has at least one virtual function, the compiler will build a virtual method table to indicate the address of these functions, If the subclass inheriting this class defines and implements a method with the same name and the same function signature, which overrides the method in the Base class, the virtual function table will point the function to the new address. At this time, polymorphism is reflected: when we point the pointer or reference of the Base class to the object of the subclass, when calling the method, we will find the method of the corresponding subclass along the virtual function table instead of the method of the Base class. Therefore, note that both Base and Sub in the following code have a virtual function "I" declared and defined_ am_ virtual_ Foo ", I use dynamic for Base and Sub of this code_ The runtime type information checked during cast conversion can be said to be this virtual function

4, reinterpret_cast

reinterpret_cast is a mandatory type converter used to handle irrelevant type conversion. It usually provides a low-level reinterpretation of the bit pattern of the operand! But he just reinterpreted the bit model of the given object, and did not carry out binary conversion!
It is used for conversion between arbitrary pointers, conversion between references, conversion between pointer and sufficiently large int type, and conversion from integer to pointer, which will be given in the article
Look at a simple code

#include<iostream>
#include<cstdint>
using namespace std;
int main() {
    int *ptr = new int(233);
    uint32_t ptr_addr = reinterpret_cast<uint32_t>(ptr);
    cout << "ptr Address of: " << hex << ptr << endl
        << "ptr_addr Value of(hex): " << hex << ptr_addr << endl;
    delete ptr;
    return 0;
}
/*
ptr Address: 0061E6D8
ptr_addr Value of (hex): 0061e6d8
*/

The above code converts the value of the address of the pointer ptr into a ptr of type unsigned int_ The integer value of addr
Provide IBM C + + reinterpret_ Places recommended by cast
A pointer to any integral type large enough to hold it
A value of integral or enum eration type to a pointer
A pointer to a function to a pointer to a function of a different type
A pointer to an object to a pointer to an object of a different type
A pointer to a member to a pointer to a member of a different class or type, if the types of the members are both function types or object types

The following example is a hash function helper from MSDN

// expre_reinterpret_cast_Operator.cpp  
// compile with: /EHsc  
#include <iostream>  

// Returns a hash code based on an address  
unsigned short Hash(void *p) {
    unsigned int val = reinterpret_cast<unsigned int>(p);
    return (unsigned short)(val ^ (val >> 16));
}
using namespace std;
int main() {
    int a[20];
    for (int i = 0; i < 20; i++)
        cout << Hash(a + i) << endl;
}

Keywords: C++ Back-end

Added by Rollo Tamasi on Sun, 06 Mar 2022 03:15:49 +0200