【C++】C++的类型转换--详解

一.C语言的类型转换

在 C 语言里,当赋值运算符两边类型不一致、形参和实参类型不匹配、或者函数返回值与接收变量类型不一样时,就会触发类型转换。

C 语言的类型转换分为两种:隐式类型转换与显式类型转换。

  • 隐式类型转换:由编译器在编译期间自动完成,可以转换就执行转换,无法转换则直接编译报错。
  • 显式类型转换:需要程序员手动编写代码完成转换。

来看代码:

cpp 复制代码
#include <stdio.h>

void Test ()
{
    int i = 1;
    // 隐式类型转换:int自动转为double
    double d = i;
    printf("%d, %.2f\n" , i, d);
 
    int* p = &i;
    // 显式强制类型转换,把指针地址转为整型
    //注意:指针转int存在平台风险,64位下建议用uintptr_t
    int address = (int) p;
 
    printf("%p, %d\n" , p, address);
}

int main(void)
{
    Test();
    return 0;
}

C语言强转缺陷:转换的可视性较差,所有转换都写成同一种形式,很难定位出错的类型转换,不利于跟踪错误。


二.为什么C++需要四种类型转换

C 风格的转换格式很简单,但是有不少缺点的:

  • 隐式类型转化有些情况下可能会出问题:比如数据精度丢失。
  • 显式类型转换将所有情况混合在一起,代码不够清晰。

因此 C++ 提出了自己的类型转化风格,注意因为 C++ 要兼容 C 语言,所以 C++ 中还可以使用 C 语言的转化风格。


三.C++ 强制类型转换

标准 C++ 为了加强类型转换的可视性,引入了四种命名的强制类型转换操作符:

  • static_cast(普通静态类型转换)
  • reinterpret_cast(二进制重新解释转换)
  • const_cast(修改常量属性)
  • dynamic_cast(多态安全向下转换,运行校验)

1.static_cast

static_cast 适用于非多态类型之间的静态转换。凡是编译器能够自动隐式完成的类型转换,都可以使用 static_cast 显式写出;但它不支持两个毫无关联的类型相互转换

cpp 复制代码
#include <iostream>
using namespace std;

int main()
{
    double d = 12.34;
    // 把double转为int,属于相近类型转换,static_cast合法
    int a = static_cast<int>(d);
    cout << a << endl;

    return 0;
}

double 向 int 转换属于合法的数值类型转换,static_cast 在编译阶段完成转换,直接舍弃小数部分,输出 12。static_cast 不支持无关类型(如指针与 int)的转换。


2.reinterpret_cast

reinterpret_cast 会直接对数据的二进制位重新解读,适合把一种完全不同的类型转换成另一种类型,不做类型安全性检查。

**注意:**这种转换仅仅改变类型解释方式,不会修改内存里的二进制内容,风险很高。

cpp 复制代码
#include <iostream>
using namespace std;

int main()
{
    double d = 12.34;
    int a = static_cast<int>(d);
    cout << a << endl;

    // static_cast不支持整型向指针转换,编译报错
    //int *p = static_cast<int*>(a);
    //强行把整型数值解释成指针地址
    int *p = reinterpret_cast<int*>(a);

    return 0;
}

整型与指针是无关类型,static_cast 不支持转换。reinterpret_cast 直接将 a 中的数值解读成指针地址,仅改变类型视角,不改动二进制数据。该转换语法可行,但得到的是无效地址,不要对指针解引用,风险较大。


3.const_cast

const_cast 主要用来移除或者增加变量的 const 属性,最常见用法是去掉 const 限定。

**注意:**它不是真正消除常量本身的只读属性,只是修改指针 / 引用的类型;如果原本是常量,强行修改会出现未定义行为。

cpp 复制代码
#include <iostream>
using namespace std;

void Test()
{
    const int a = 2;
    //去掉const属性,用普通指针接收常量的地址
    int* p = const_cast<int*>(&a);
    *p = 3;
    cout << a << endl;
}

int main()
{
    Test();
    return 0;
}

普通指针无法接收 const 变量的地址,借助 const_cast 去除指针的 const 限制。但 a 本身是常量,通过指针修改常量属于未定义行为,运行结果不确定。const_cast 仅能增减 const 属性,不支持其他类型转换。

补充实用场景:它更适合本来不是常量,只是被 const 指针接收的情况,这种修改是安全的。如果你想要,我可以写一段安全使用 const_cast 的示例。


4.dynamic_cast

dynamic_cast 用来完成父类指针或引用向子类指针或引用的动态转换。

  • 向上转型:子类指针 / 引用转为父类指针 / 引用,依靠赋值兼容规则,不需要强制转换。
  • 向下转型:父类指针 / 引用转为子类指针 / 引用,使用 dynamic_cast 可以实现安全转换。

注意: dynamic_cast 只能用在包含虚函数的父类中。 转换时它会先做运行时检查,如果转换合法就正常转换;转换失败,指针会返回空指针 0。

cpp 复制代码
#include <iostream>
using namespace std;

class A
{
public:
    virtual void f() {}
};

class B : public A
{
};

void fun(A* pa)
{
    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;
}
  1. A 类中写了虚函数,这是 dynamic_cast 能够正常使用的前提。
  2. 第一次调用 fun (&a),传进去的是父类对象地址。 static_cast 不会在运行时校验对象真实类型,直接强制转为 B 类指针,拿到的指针是无效的; dynamic_cast 运行时检测出原始对象是父类,向下转换行不通,直接返回空指针。
  3. 第二次调用 fun (&b),传入子类对象地址。 两种转换都能成功得到子类指针。
  4. static_cast 只在编译期间做简单检查,向下转型存在安全隐患;dynamic_cast 依靠运行时类型判断保证安全,多用于多态代码。

补充:强制类型转换会跳过常规的类型检查。写代码时,优先想想有没有不用强制转换的方案。实在不得不使用强制转换,尽量缩小它的作用范围,减少 bug 出现的可能性。


四.RTTI(了解)

RTTI:Run-time Type identification 的缩写,即运行时类型识别,用来在程序运行阶段判断对象的真实类型。

C++ 通过下面几个方式支持 RTTI:

  • typeid 运算符:返回一个 type_info 对象,用来查询表达式的实际类型。
  • dynamic_cast 运算符:前面讲过,用于多态下安全向下转型,能转就转,不能转返回空指针。
  • decltype:在编译阶段推导表达式的类型,主要配合模板和泛型编程使用。
    补充:RTTI 需要依赖虚函数表来记录类型信息,所以想要用 RTTI 判断类型,前提是类里面包含虚函数

通俗理解就是:typeid 负责 "看是什么类型",dynamic_cast 负责 "能不能安全转",decltype 则偏编译期推导,三者应用场景不太一样。

相关推荐
计科杨某人2 小时前
简单算法题(基础入门题)
c++·算法·题解·入门·基础算法
Tairitsu_H11 小时前
[C++] 深入理解红黑树:封装set与map
开发语言·c++·set·map·红黑树·模拟实现
HugoStudio_SWAN12 小时前
洛谷 B4450 / B3867 / B3923 智慧购物、储蓄与做题——从程序到生活
c++·学习·程序人生·算法·生活
会周易的程序员14 小时前
5Draft(五帝)测试报告
服务器·c++·分布式·raft·共识算法·共识·算力服务器
UIU11414 小时前
scanf与cout的误区:探究其内部的机制
c++·学习·c#·scanf
闻缺陷则喜何志丹14 小时前
【栈 运算系统】P3719 [AHOI2017初中组] rexp|普及+
c++·算法··洛谷·运算系统
bnmoel15 小时前
C++ 基础入门篇(一):命名空间,输入&输出,缺省参数,函数重载
c++·函数重载·语法·命名空间·缺省参数
Zixhy15 小时前
多点巡检机器人项目技术总结
linux·c++·机器人·自动驾驶
Pointer Pursuit17 小时前
C++11特性(二)
前端·c++·算法