文章

C++类型转换

C++类型转换分为隐式和显式,显式有static_cast、const_cast等,安全高效管理类型转换。

C++类型转换

C++类型转换

C++ 中的类型转换(Type Conversion)是指将一种数据类型的值转换为另一种数据类型。C++ 提供了 隐式类型转换显式类型转换 两种方式,另外引入了更安全和可控的 C++ 风格的类型转换

一、隐式类型转换(Implicit Conversion)

编译器自动完成的类型转换,主要发生在以下情况:

  1. 算术运算中不同类型的转换

    1
    2
    3
    
    int a = 10;
    double b = 3.14;
    double c = a + b; // a 被自动转换为 double
    
  2. 函数参数传递时类型不匹配

    1
    2
    
    void func(double x);
    func(5); // 5 是 int,会被自动转换为 double
    
  3. 赋值语句中类型不同

    1
    
    int i = 3.14; // double -> int,发生截断
    

二、显式类型转换(Explicit Conversion)

显式转换由程序员主动触发,常见形式:

1. C 风格的强制类型转换(不推荐)

1
2
double d = 3.14;
int i = (int)d;

2. C++ 风格的类型转换(推荐)

更安全、语义更清晰。主要包括以下四种:

三、C++ 类型转换运算符

1. static_cast<T>(expression)(静态转换)

用于 基本类型之间的转换,或类之间 存在继承关系且已知安全的转换

1
2
3
4
5
6
7
int i = 10;
double d = static_cast<double>(i); // int -> double

class Base {};
class Derived : public Base {};
Base* b = new Derived();
Derived* d = static_cast<Derived*>(b); // 已知 b 实际上指向 Derived*

2. dynamic_cast<T>(expression)(动态转换)

用于 运行时类型检查的指针或引用转换,只能用于多态类型(即有虚函数的类)。

1
2
3
4
5
class Base { virtual void foo() {} };
class Derived : public Base {};

Base* b = new Derived();
Derived* d = dynamic_cast<Derived*>(b); // 安全转换

如果转换失败,指针返回 nullptr,引用会抛出 std::bad_cast

3. const_cast<T>(expression)(常量转换)

用于添加或去除 constvolatile 属性。

1
2
3
4
5
// 这里 new int(10) 创建了一个 非 const 的 int,值是 10
// 但是用一个 const int* 指针来接收它,意思是承诺不通过 p 来修改它的值
const int* p = new int(10);
// 使用 const_cast 去掉了 p 的 const 限定,使得 q 成为了可以修改的普通指针
int* q = const_cast<int*>(p);
  • const int* p 只是承诺不改,但指向的其实是一个原本可以修改的对象new int(10) 创建的是普通 int)。

  • 也就是说,虽然声明时用了 const 修饰指针类型,但没有真正地创建一个 const 对象,所以这时候能用 *q = 20 来修改值。

使用 const_cast 修改数据,必须保证原始对象不是 const 的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include <iostream>

int main() {
    const int a = 10;               // 声明一个 const int 类型的变量 a,值为 10,不允许修改
    const int* p = &a;              // 声明一个指向 const int 的指针 p,指向 a,表示不能通过 p 修改 a

    int* q = const_cast<int*>(p);   // 使用 const_cast 去掉指针的 const 限定符,将 p 转换为 int* 类型
                                    // 注意:虽然语法上合法,但如果修改的是一个真正的 const 对象(比如 a),则行为是未定义的!

    *q = 20;                        // 尝试通过 q 修改 a 的值为 20 —— 未定义行为!
                                    // 在某些编译器中,这可能会导致程序崩溃、数据不变、或其他不可预测的结果

    std::cout << "a = " << a << std::endl;  // 输出 a 的值。由于 a 是 const,编译器可能将它优化为常量值输出,结果仍为 10

    return 0;
}
  • const_cast 只能用来修改那些原本不是 const,但被“临时”视为 const 的对象。

  • 修改原本就声明为 const 的对象(如 const int a = 10)的值是未定义行为(Undefined Behavior)。

  • 这种修改行为可能在某些平台或编译器下看似“成功”,但那是偶然的,不可依赖。

4. reinterpret_cast<T>(expression)(重新解释转换)

极端转换,将数据的二进制表示强制解释为另一种类型。

1
2
int* p = new int(10);
char* c = reinterpret_cast<char*>(p); // 直接 reinterpret 成 char*

通常用于底层编程,需特别小心,避免未定义行为。

四、总结对比

类型转换检查时机适用范围安全性用法示例
隐式转换 基本类型、兼容类型double d = 5;
C 风格强制转换 所有类型int i = (int)3.14;
static_cast编译时编译期可判断的类型转换中高double d = static_cast<double>(i);
dynamic_cast运行时多态类的指针/引用转换Derived* d = dynamic_cast<Derived*>(b);
const_cast编译时修改 const/volatile 属性int* q = const_cast<int*>(p);
reinterpret_cast无检查底层二进制转换char* c = reinterpret_cast<char*>(p);

最佳实践建议:

  1. 优先使用 static_cast
  2. 多态类型向下转型使用 dynamic_cast
  3. 除非必要,避免使用 const_castreinterpret_cast
  4. 完全避免使用C风格强制转换 (type)expr
  5. 对不确定的转换,添加运行时检查
1
2
3
4
5
6
// 安全转换示例
if (Derived* d = dynamic_cast<Derived*>(basePtr)) {
    // 转换成功,安全使用d
} else {
    // 处理转换失败
}
本文由作者按照 CC BY 4.0 进行授权