C++| |四种强制类型转化(剖析)

四种强制类型转换


1. 出现的原因

C语言的强制类型转换,有着两种。

  1. 隐式类型转换

  2. 显示的强制类型转换

举例:

int main()
{
    int i = 1;
    double d = i;//隐式类型转换
    int* p = &i;
    int address = (int)p;//显示强制类型转换
    return 0;
}

缺陷:

  • 转换的可视化比较差,对于隐式类型转换来说

  • 对于所有的转化形式都是使用()类型书写的,却别程度不高

所以对于C++来说,为了加强类型转化的可视性,出现了四种强制类型转化

可以根据名字直接判断出是进行的什么类型的转化。

2. 四种类型转换分别是什么

1. static_cast

作用:用于非多态类型之间的转化(静态转化),等价于C语言中的隐式类型转化

缺点:不能用于两个不相关类型之间的转化。比如:int类型到int*类型的转化

举例:

int main()
{
    int i = 1;
    double d = static_cast<double>(i);
    std::cout << d << std::endl;
    retrun 0;
}

2. reinterpret_cast

作用:将一种类型转化为另外一种类型。等价于C语言中的显示强制类型转化

缺点:对于数据随意的转化,会出现无法预估的问题。尽量不要使用

举例:

int main()
{
    int i = 10;
    int* p = &i;
    int j = reinterpret_cast<int>(p);
    return 0;
}

3. const_cast

作用:删除变量的const属性,方便赋值

缺点:由于编译器的优化,就会导致访问const类型的变量的时候,不能实时的访问,有可能内存的值已经发生改变,但是访问到的还是旧的数据

解决:使用volatile关键字,确保内存的可见性,每次访问一个数据的时候到内存中直接去拿取数据

举例:

int main()
{
    const int i = 10;
    int* p = const_cast<int*>(&a);
    *p = 20;
    std::cout << a << std::endl;
    return 0;
}

4. dynamic_cast

作用:用于将一个父类对象的指针转化为一个子类对象的指针或者引用

缺点:当父类指针本来指向的是父类对象的时候,转化之后,使用子类类型的指针进行操作的时候,有可能造成内存访问越界

改善:只有当父类指针指向的就是子类对象的时候,在使用dynamic_cast强制类型转换

注意:

  1. dynamic_cast只能用于含有虚函数的类

  2. dynamic_cast会先检查是否能转换成功,能成功则转化,不能则返回0

对于父类指针指向的是父类对象会出错,返回0

举例:

class A
{
    public :
    virtual void f(){}
};
​
class B : public A
    {};
​
void fun (A* pa)
{
    // dynamic_cast会先检查是否能转换成功,能成功则转换,不能则返回
    B* pb1 = static_cast<B*>(pa);
    B* pb2 = dynamic_cast<B*>(pa);
    cout<<"pb1:" <<pb1<< endl;
    cout<<"pb2:" <<pb2<< endl;
}
​
int main ()
{
    A a;
    B b;
    fun(&a);
    fun(&b);
    return 0;
}

3. 底层原理

底层原理有着两种:

  1. 数据是真正的发生改变了,在内存中新开辟了一个空间用于存储新数据,使用新数据的时候,不会影响到原有数据

  2. 数据没有发生改变,只是类型改变了,让编译器将这个类型的数据可以认为是另外一种类型的数据,只是数据合法了

static_cast是第一种类型:

根据上面的代码:

可以明显的看到对于两个数据,其地址不一样了,所以是开辟可一个新的内存空间,用来存放新的数据

reinterpret_cast是第一种类型:

根据上面的代码:

可以看到开辟新的内存空间了。

对于函数类型之间的转化也是开辟了一个新的函数,地址发生了改变。

举例:

using namespace std;
typedef void(*FUNC)();
int DoSomething(int i)
{
    cout << "DoSomething" << endl;
    return 0;
}
void Test()
{
    //
    // reinterpret_cast可以编译器以FUNC的定义方式去看待DoSomething函数
    // 所以非常的BUG,下面转换函数指针的代码是不可移植的,所以不建议这样用
    // C++不保证所有的函数指针都被一样的使用,所以这样用有时会产生不确定的结果
    //
    FUNC f = reinterpret_cast< FUNC>(DoSomething);
    f();
}
​
int main()
{
    Test();
    return 0;
}

了解:对于一个函数来说,一个函数也就是一个地址,当调用这个函数的时候,只需要函数名即可

可以看到两个的地址已经不一样了,所以发生了改变

const_cast是第二种类型:

注意一点:const_cast<>显示强制类型转换的时候,必须是指针,引用或者是指向对象类型成员的指针

进行强制类型转化之后,没有创建新的数据,没有开辟空间来存放指针,编译器只是将一种类型的指针当做了另外一种类型对的指针,确保了代码的合法性而已

根据上面的代码:

没有添加volatile:

添加volatile:

dynamic_cast是第二种类型

只是对于指针发生了改变,编译器对于解释的时候发生变化,没有开辟内存空间创建新的指针

根据上面的代码:

从该图可以看出,当父类指针指向的是父类对象的时候,进行dynamic_cast转化的时候,就会转化出错,返回0,所以第一次输出pb2的时候,其指针变量的值就是全0。

猜你喜欢

转载自blog.csdn.net/qq_40399012/article/details/89056512