在 C++ 中,提供了四种主要的类型转换操作符(cast),它们统称为 _cast 函数,用于在不同类型之间进行显式类型转换。这些操作符分别是 static_castdynamic_castconst_castreinterpret_cast。它们取代了 C 风格的强制类型转换(如 (type)expression),提供了更安全、更清晰的类型转换方式。以下是对这四个 _cast 函数的详细讲解,包括它们的用途、语法、特点以及使用场景。

static_cast

定义

static_cast 是一种在编译时进行类型转换的操作符,用于执行相对安全的类型转换。它不会进行运行时类型检查,因此效率较高,但需要程序员确保转换的正确性。

语法

1
static_cast<目标类型>(表达式)

用途

  • 基本数据类型转换:在内置类型之间进行转换,例如 intfloat、指针类型之间的转换(在安全的情况下)。
  • 类层次结构中的向上转换:将派生类指针或引用转换为基类指针或引用(这是安全的,因为派生类对象总是包含基类部分)。
  • 调用显式构造函数或转换函数:用于调用用户定义的类型转换(例如通过构造函数或转换运算符)。
  • 枚举类型与整数类型之间的转换

特点

  • 编译时检查static_cast 在编译时完成类型转换,不涉及运行时开销。
  • 安全性:适用于明确知道类型关系的场景。程序员需要确保转换是合法的,否则可能导致未定义行为。
  • 不支持不相关类型之间的转换:不能直接将不相关的指针类型(如 int*double*)转换。

示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#include <iostream>
using namespace std;

class Base {};
class Derived : public Base {};

int main() {
// 1. 基本类型转换
int i = 10;
double d = static_cast<double>(i); // int 转为 double
cout << "d = " << d << endl; // 输出:d = 10

// 2. 向上转换(派生类到基类)
Derived derived;
Base* base_ptr = static_cast<Base*>(&derived); // 安全

// 3. 调用用户定义的转换
struct MyType {
operator int() { return 42; } // 转换函数
};
MyType mt;
int val = static_cast<int>(mt); // 调用 operator int
cout << "val = " << val << endl; // 输出:val = 42

return 0;
}

注意事项

  • static_cast 不能去除 const 属性(需要用 const_cast)。
  • 不支持运行时类型检查,因此不能用于多态类型之间的向下转换(派生类到基类的转换需要 dynamic_cast)。

dynamic_cast

定义

dynamic_cast 用于在类层次结构中进行安全的类型转换,主要用于多态类型的指针或引用转换。它在运行时进行类型检查,依赖于运行时类型信息(RTTI,Run-Time Type Information)。

语法

1
dynamic_cast<目标类型>(表达式)

用途

  • 多态类型之间的向下转换:将基类指针或引用转换为派生类指针或引用,检查对象是否确实是目标类型。
  • 运行时类型检查:确保转换的安全性,如果转换失败,返回 nullptr(对于指针)或抛出 bad_cast 异常(对于引用)。

特点

  • 运行时检查:依赖 RTTI,需要类具有虚函数(多态性)。
  • 安全性:如果转换不合法,dynamic_cast 会返回 nullptr(指针)或抛出异常(引用)。
  • 性能开销:由于运行时检查,dynamic_caststatic_cast 慢。
  • 限制:只适用于具有虚函数的类(多态类型),否则编译失败。

示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
#include <iostream>
using namespace std;

class Base {
public:
virtual ~Base() {} // 虚函数,确保多态
};

class Derived : public Base {};

int main() {
Base* base = new Derived;

// 1. 向下转换(基类到派生类)
Derived* derived = dynamic_cast<Derived*>(base);
if (derived) {
cout << "Conversion succeeded" << endl;
} else {
cout << "Conversion failed" << endl;
}

// 2. 失败的转换
Base* base2 = new Base;
Derived* derived2 = dynamic_cast<Derived*>(base2);
if (derived2) {
cout << "Conversion succeeded" << endl;
} else {
cout << "Conversion failed" << endl; // 输出:Conversion failed
}

// 3. 引用转换(失败会抛异常)
Base& base_ref = *base;
try {
Derived& derived_ref = dynamic_cast<Derived&>(base_ref);
cout << "Reference conversion succeeded" << endl;
} catch (const bad_cast& e) {
cout << "Reference conversion failed: " << e.what() << endl;
}

delete base;
delete base2;
return 0;
}

注意事项

  • 确保类层次结构中至少有一个虚函数,否则 dynamic_cast 会导致编译错误。
  • 使用 dynamic_cast 时,通常需要检查返回的指针是否为 nullptr 或捕获引用转换的异常。
  • 如果不需要运行时检查,优先使用 static_cast 以提高性能。

const_cast

定义

const_cast 用于添加或移除变量的 constvolatile 限定符。它的主要作用是修改类型的常量性或易变性。

语法

1
const_cast<目标类型>(表达式)

用途

  • 移除 constvolatile 属性:允许修改原本被声明为 const 的对象(但前提是对象本身不是真正的 const)。
  • const 不匹配的函数调用:用于传递参数给不接受 const 参数的函数。

特点

  • 仅影响 constvolatile 限定符:不会更改底层类型(如将 int* 转为 double*)。
  • 潜在危险:如果对真正的 const 对象进行修改,会导致未定义行为。
  • 典型场景:用于调用遗留代码中不接受 const 参数的函数。

示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#include <iostream>
using namespace std;

void modify(int* ptr) {
*ptr = 100;
}

int main() {
// 1. 移除 const 属性
const int val = 10;
int* ptr = const_cast<int*>(&val);
// modify(ptr); // 未定义行为!val 是真正的 const 对象

// 2. 合法的 const_cast 使用
int x = 20;
const int* const_ptr = &x; // 指向 x 的 const 指针
int* mutable_ptr = const_cast<int*>(const_ptr); // 移除 const
*mutable_ptr = 30; // 合法,因为 x 本身不是 const
cout << "x = " << x << endl; // 输出:x = 30

// 3. 传递给非 const 参数的函数
modify(const_cast<int*>(const_ptr));
cout << "x = " << x << endl; // 输出:x = 100

return 0;
}

注意事项

  • 未定义行为:对真正的 const 对象(如 const int val)进行修改会导致未定义行为。
  • 谨慎使用:仅在明确知道对象非 const 或需要与旧代码接口兼容时使用。
  • const_cast 通常用于指针或引用,不能用于基本类型的直接转换。

reinterpret_cast

定义

reinterpret_cast 是最不安全的类型转换操作符,用于在不相关类型之间进行低级别的重新解释。它不检查类型安全性,完全依赖程序员的判断。

语法

1
reinterpret_cast<目标类型>(表达式)

用途

  • 指针类型之间的转换:将一种类型的指针转换为另一种类型的指针(如 int*char*)。
  • 指针与整数之间的转换:将指针转换为整数类型(如 uintptr_t)或反之。
  • 处理硬件相关代码:常用于嵌入式系统或与硬件交互的场景。
  • 函数指针转换:将一种函数指针类型转换为另一种函数指针类型。

特点

  • 不安全reinterpret_cast 不保证转换后指针的有效性,结果可能不可移植或导致未定义行为。
  • 底层操作:直接重新解释二进制位模式,不进行任何类型检查。
  • 跨平台差异:转换结果可能依赖于平台(如指针大小、字节序)。

示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
#include <iostream>
#include <cstdint>
#include <new>
using namespace std;

int main() {
// 1. 指针类型转换
int x = 42;
int* int_ptr = &x;
char* char_ptr = reinterpret_cast<char*>(int_ptr); // int* 转为 char*
cout << "char_ptr points to: " << static_cast<void*>(char_ptr) << endl;

// 2. 指针与整数转换
uintptr_t int_val = reinterpret_cast<uintptr_t>(int_ptr);
cout << "int_ptr as integer: " << int_val << endl;

// 3. 函数指针转换
void (*func_ptr)() = [](){ cout << "Hello" << endl; };
using NewFuncType = void(*)();
NewFuncType new_func = reinterpret_cast<NewFuncType>(func_ptr);
new_func(); // 输出:Hello

// 4. 原始缓冲区与对象类型之间的转换(placement new,需要保证对齐)
alignas(alignof(long)) unsigned char buf[sizeof(long)];
// placement new 并没有申请内存,所以不需要 delete
long* pLong = new (buf) long{123456789L}; // 在已对齐的原始内存上构造对象
unsigned char* bytes = reinterpret_cast<unsigned char*>(pLong); // 以字节视图访问
cout << "first byte = " << static_cast<int>(bytes[0]) << endl;

return 0;
}

注意事项

  • 未定义行为风险:错误使用 reinterpret_cast 可能导致程序崩溃或不可预测的结果。
  • 平台依赖性:转换结果可能在不同平台上表现不同,需谨慎使用。
  • 尽量避免:除非在低级别编程(如驱动开发、嵌入式系统)中,否则应尽量避免使用 reinterpret_cast

std::anystd::any_cast

std::any(C++17)是一个类型擦除容器,std::any_cast 用于从中取回值。严格来说它们并不属于“类型转换 cast”家族,而是独立的类型工具。关于 std::any 的完整用法、类型擦除原理,以及与之配对的 std::variant,请参阅 C++17 any 与 variant

比较与总结

转换类型用途安全性运行时检查典型场景
static_cast基本类型转换、向上转换、用户定义转换中等类型明确、安全的转换
dynamic_cast多态类型的向下转换类层次结构中的运行时类型检查
const_cast移除/添加 constvolatile 限定符处理 const 不匹配的遗留代码
reinterpret_cast不相关类型的低级别转换硬件相关、指针/整数转换

选择建议

  1. 优先使用 static_cast:在类型关系明确且安全的场景下使用,性能较高。
  2. 使用 dynamic_cast 处理多态:当需要运行时类型检查时,特别是在类层次结构中。
  3. 谨慎使用 const_cast:仅在处理 const 不匹配的场景(如遗留代码)时使用。
  4. 尽量避免 reinterpret_cast:除非涉及底层编程或明确知道后果。