问小白 wenxiaobai
资讯
历史
科技
环境与自然
成长
游戏
财经
文学与艺术
美食
健康
家居
文化
情感
汽车
三农
军事
旅行
运动
教育
生活
星座命理

C++类型转换详解:从C风格到C++风格

创作时间:
作者:
@小白创作中心

C++类型转换详解:从C风格到C++风格

引用
1
来源
1.
https://www.jindouyun.cn/document/industry/details/184333

本文主要介绍了C++中的类型转换相关内容,包括C风格的类型转换和C++风格的类型转换,以及RTTI(运行时类型识别)的相关知识。文章内容详细,涵盖了隐式类型转换、显式类型转换、static_cast、dynamic_cast、const_cast、reinterpret_cast等知识点,并通过代码示例进行了说明。本文适合C++编程初学者阅读。

一、C风格类型转换

1.1 隐式类型转换

隐式类型转换(Implicit Type Conversion),又称自动类型转换,由编译器在编译阶段自动进行,能转就转,不能转就编译失败。

常见的隐式类型转换包括:

  • 赋值运算符中的类型转换:例如从int赋值转换为double。
  • 算术运算符中的类型转换:例如int与long进行运算,会被提升为long。同时,超出类型的表示范围,会进行符号扩展或截断。
  • 函数调用中的类型转换:实参与形参类型不匹配,会尝试转换为与形参匹配的类型。
  • 布尔值中的类型转换:对于数值类型,非零值转换为true,零值转换为false;对于指针类型,非空指针转换为true,空指针转换为false。
  • 指针中的类型转换:指针可以隐式转换为void*类型。数组名会被转换为指向数组第一个元素的指针,函数名会被转换为指向该函数的指针。
void func(double d){}  
void test() {  
    int i = 1;  
    double d = i;//赋值运算符  
    double ret = i + d;//算术运算符  
    func(i);//函数调用  
    void* p = &i;//指针  
}  
1.2 显式类型转换

显式类型转换(Explicit Type Conversion),又称为强制类型转换,需要程序员显式地指定转换方式。

格式:(type)expression

void test() {  
    int i = 1;  
    double d = (double)i;  
}  

二、C++风格类型转换

2.1 static_cast

格式:static_cast(expression)

用于非多态类型的转换(静态转换),并且只适用相近类型之间的转换,对应C的隐式类型转换,例如基本数据类型之间的转换。

void test() {  
    int i = 1;  
    double d = static_cast<double>(i);//静态转换,相近类型  
    int* pi = &i;  
    void* p = (pi);  
}  
2.2 dynamic_cast

格式:dynamic_cast(expression)

用于多态类型的转换(动态转换),并且用于向下转型:将一个父类对象的指针/引用转换为子类对象的指针或引用。

ps:向上转型,没有类型转换,属于赋值兼容规则。

class A {  
public:  
    virtual void f(){}  
    int _a = 1;  
};  

class B :public A {  
public:  
    virtual void f(){}  
    int _b = 2;  
};  

void test() {  
    B b;  
    A a = b;  
    A& ra = b;//此处没有隐式类型转换  
    double d = 3.14;  
    const int& ri = d;//此处有隐式类型转换  
}  

由于类型转换后的临时对象具有常性,所以要用常引用。而向上转型(切片)不用常引用,所以没有发生类型转换。

dynamic_cast 在运行时检查转换是否有效。如果转换无效,则指针类型返回空指针nullptr,引用类型抛出异常std::bad_cast。

void func(A* pa) {  
    //B* p = (B*)pa;//直接向下转换,如果指向父类,则存在越界风险  
    B* p = (pa);  
    if (p)  
    {  
        p->_a++;  
        p->_b++;  
    }  
    else  
    {  
        cout << "转换失败" << endl;  
    }  
}  

void test() {  
    B b;  
    A a = b;  
    func(&a);  
    func(&b);  
}  

直接向下转换,如果指向父类,则存在越界风险。运用 dynamic_cast 则可安全地进行向下转型。

2.3 const_cast

格式:const_cast(expression)

用于修改对象的常量属性,可添加或移除变量的const或volatile属性。

void test() {  
    const int a = 2;  
    int* p = (&a);  
    *p = 3;  

    cout << a << endl;  
    cout << *p << endl;  

    cout << &a << endl;  
    cout << p << endl;  
}  

通过打印可以发现,a的值依旧为2,而p的值为3,并且它们的地址相同。这是为什么呢?其实,编译器在这里做了优化,常变量a会被放进寄存器或者*直接替换

这时我们可以加上volatile关键字,其作用是阻止编译器对标记的变量进行某些优化。

void test() {  
    volatile const int a = 2;  
    int* p = (&a);  
    *p = 3;  

    cout << a << endl;  
    cout << *p << endl;  

    //cout << &a << endl;  
    printf("%p\n", &a);  
    cout << p << endl;  
}  

此时还有一个问题,那就是用volatile修饰的变量类型无法和参数为void*的operator<<函数匹配,从而会匹配到不合适的重载函数,输出错误的地址值。所以,此处我们用printf进行打印即可。

2.4 reinterpret_cast

格式:reinterpret_cast(expression)

为操作数的位模式提供较低层次的重新解释。用于不相近类型之间的转换,对应C的显式类型转换。

void test() {  
    int i = 1;  
    int* p = &i;  
    //double* p1 = static_cast<double*>(i);//error  
    double* p2 = reinterpret_cast<double*>(i);  
    //double* p3 = static_cast<double*>(p);//error  
    double* p4 = reinterpret_cast<double*>(p);  
}  

三、RTTI

RTTI(Run-Time Type Identification)是C++语言中的一个特性,它允许程序在运行时确定对象的实际类型。

3.1 typeid

用于打印变量的类型。

void test() {  
    auto f = [](int x, int y){return x + y;};  
    cout << typeid(f).name() << endl;  
}  
3.2 dynamic_cast
class Base {};  
class Derive :public Base {};  

void func(Base* pb) {  
    Derive* pd = dynamic_cast<Derive*>(pb);  
}  
3.3 decltype

可将变量的类型提取,作为参数传递。

void test() {  
    int x = 1;  
    double y = 2.0;  

    auto ret = x * y;  
    vector<decltype(ret)> v;  
}  

总结

C风格的类型转换,将不同类型的转换混在一起,不够清晰,可视性差,难以跟踪错误的转换。所以我们推荐使用C++的类型转换,加强类型转换的可视性,提高代码的可读性与可维护性。

一般来说:

  • 使用static_cast进行相近类型的转换。
  • 使用dynamic_cast在运行时进行多态类型检查。
  • 使用const_cast移除或添加常量性。
  • 使用reinterpret_cast进行不相近类型的转换。

© 2023 北京元石科技有限公司 ◎ 京公网安备 11010802042949号