2More Effective C++

类型转换

强制类型转换

static_castdynamic_castconst_castreinterpret_cast

🎯 AI 时代的学习建议:不要只读理论。理解这一章的设计动机(为什么需要这个概念?)和边界条件(什么情况下会失效?)。当 AI 生成的代码涉及这些概念时,你能判断它是否用对了场景。AI 能帮你总结内容,但无法替代你亲手阅读、思考、建立直觉的过程。

第2章:类型转换(Type Conversions)

一、导读

1.1 本章学习目标

第2章讲解 C++ 的四种类型转换操作符:static_cast、dynamic_cast、const_cast、reinterpret_cast。Meyers 强调应该用这些操作符替代 C 风格的强制转换。

本章核心学习目标:

  • 掌握四种类型转换的用途和限制
  • 理解为什么应该避免 C 风格转换
  • 掌握 dynamic_cast 的安全向下转型

1.2 为什么本章如此重要

类型转换是 C++ 中最容易出错的区域之一。C 风格的转换可以绕过类型系统,导致难以发现的错误。C++ 的四种转换操作符提供了更安全、更明确的替代方案。

1.3 核心问题引导

static_cast 和 dynamic_cast 有什么区别?

什么时候应该使用 const_cast?

为什么 reinterpret_cast 如此危险?


二、核心概念详解

2.1 四种类型转换

cpp
// 1. static_cast: 编译时类型转换
int i = 42;
double d = static_cast<double>(i);  // int → double

Base* base = new Derived;
Derived* derived = static_cast<Derived*>(base);  // 向下转型(不安全!)
// static_cast 不检查运行时类型,如果 base 实际不是 Derived,行为未定义

// 2. dynamic_cast: 运行时安全的向下转型
Base* base = new Derived;
if (Derived* derived = dynamic_cast<Derived*>(base)) {
    // 转换成功,base 确实是 Derived
    derived->derivedMethod();
} else {
    // 转换失败,base 不是 Derived
}
// 要求: 基类必须有虚函数
// 代价: 运行时开销(RTTI)

// 3. const_cast: 移除 const/volatile
const int* cp = &i;
int* p = const_cast<int*>(cp);  // 移除 const
// 通过 p 修改 i 是未定义行为(如果 i 本身是 const)

// 4. reinterpret_cast: 低级别的位模式转换
int* ptr = new int(42);
intptr_t addr = reinterpret_cast<intptr_t>(ptr);  // 指针 → 整数
void (*func)() = reinterpret_cast<void(*)()>(0x1234);  // 整数 → 函数指针
// 极度危险!几乎总是错误的

2.2 替代 C 风格转换

cpp
// C 风格转换: (type)expression
// 等价于依次尝试:
// 1. const_cast
// 2. static_cast
// 3. static_cast + const_cast
// 4. reinterpret_cast

int i = 42;
double d = (double)i;        // C 风格
double d2 = static_cast<double>(i);  // C++ 风格(明确意图)

// C++ 风格的优势:
// 1. 容易识别和搜索(grep 友好)
// 2. 明确表达转换意图
// 3. 编译器可以做更多检查

// 避免 C 风格转换:
class Base { virtual ~Base() = default; };
class Derived : public Base { };

Base* b = new Derived;
// Derived* d = (Derived*)b;  // C 风格: 可能是 static_cast 或 reinterpret_cast
Derived* d = static_cast<Derived*>(b);  // 明确: 我知道这是向下转型
Derived* d2 = dynamic_cast<Derived*>(b);  // 更安全: 运行时检查

三、实战案例

3.1 案例一:dynamic_cast 的安全使用

cpp
class Shape {
public:
    virtual ~Shape() = default;
    virtual void draw() = 0;
};

class Circle : public Shape {
public:
    void draw() override { /* 画圆 */ }
    double radius() const { return 5.0; }
};

class Rectangle : public Shape {
public:
    void draw() override { /* 画矩形 */ }
    double width() const { return 10.0; }
};

void processShape(Shape* shape) {
    // 安全地向下转型
    if (auto* circle = dynamic_cast<Circle*>(shape)) {
        std::cout << "Circle with radius " << circle->radius() << "\n";
    } else if (auto* rect = dynamic_cast<Rectangle*>(shape)) {
        std::cout << "Rectangle with width " << rect->width() << "\n";
    }
    
    // 或者使用 visitor 模式避免 dynamic_cast
}

四、本章小结

static_cast 用于编译时安全的类型转换

dynamic_cast 用于运行时安全的向下转型(需要虚函数)

const_cast 仅用于移除 const,谨慎使用

reinterpret_cast 极度危险,几乎总是错误的


五、思考与练习

思考题

dynamic_cast 的运行时开销有多大?

什么时候应该用 visitor 模式替代 dynamic_cast?

编程练习

实现一个类型安全的 Any 类,内部使用 dynamic_cast 或 type_info。

重写一段使用 C 风格转换的代码,改为使用 C++ 转换操作符。


2/6类型转换
0%