Skip to content

1. 类型转换和异常

1.1 类型转换

1.1.1 C语言中的类型转换

  • 隐式类型转换场景

    • 整数提升:当较小的整数类型(如char、short)参与运算时,会被隐式地转换为较大的整数类型(如int)。
      cpp
      char c = 'a';
      int a = 1;
      cout << "a + c = " << a + c << endl;
    • 浮点数提升:当较小的浮点数类型(如float)与较大的浮点数类型(如double)进行运算时,会被隐式地转换为较大的浮点数类型。
      cpp
      float num1 = 1.1f;
      double num2 = 1.2;
      cout << "num1 + num2 = " << num1 + num2 << endl;
    • 整数和浮点数的混合运算:当整数和浮点数参与运算时,整数会被隐式地转换为浮点数。
      cpp
      int a = 1;
      double c = 2.0;
      cout << "a / c = " << a / c << endl; // 输出 0.5
    • 自动类型转换:表达式中的不同类型操作数参与运算时,编译器自动进行类型转换。
      cpp
      float num1 = 1;  // int 隐式转 float
      int num2 = 2;
      cout << "num1 / num2 = " << num1 / num2 << endl; // float / int → float
  • 函数参数传递:实参与形参类型不匹配时,编译器尝试隐式转换。

    cpp
    void fun(float num1, float num2) {
        cout << "num1 / num2 = " << num1 / num2 << endl;
    }
    
    int main() {
        int num1 = 1;
        int num2 = 2;
        fun(num1, num2); // int 隐式转 float
        return 0;
    }

显示类型转换

程序员手动进行类型转换。
格式
目标类型 变量 = (目标类型)表达式;
示例

cpp
double d = 1.2;
int a = (int)d; // double → int

int *p = (int *)malloc(sizeof(int)); // void* → int*

1.1.2 C++中的类型转换

C++新增4种类型转换操作符:

  1. 静态类型转换 (static_cast)
    基本类型转换,编译时检查。
    语法目标类型变量 = static_cast<目标类型>(源类型变量);
    示例

    cpp
    char c = 'a';
    int a = static_cast<int>(c); // char → int
    void* p = &a;
    int* p_a = static_cast<int*>(p); // void* → int*

    使用场景

    • 基础类型转换(如 int → double)
    • 指针类型转换(需兼容)
  2. 动态类型转换 (dynamic_cast)
    用于继承关系的类型转换(运行时检查)。
    语法目标类型变量 = dynamic_cast<目标类型>(源类型变量);
    要求:类必须有虚函数。
    使用场景:基类与派生类指针/引用转换。

  3. 常量类型转换 (const_cast)
    移除 const 属性。
    语法目标类型变量 = const_cast<目标类型>(源类型变量);
    示例

    cpp
    int a = 10;
    const int* p_a = &a;
    int* p_b = const_cast<int*>(p_a); // 移除 const
    *p_b = 11; // 允许修改
    
    const int& r_a = a;
    int& r_b = const_cast<int&>(r_a); // 常引用转普通引用
    r_b = 20;

    使用场景:去除指针/引用的 const 属性。

  4. 重解释类型转换 (reinterpret_cast)
    任意类型强制转换(高风险)。
    语法目标类型变量 = reinterpret_cast<目标类型>(源类型变量);
    示例

    cpp
    double b = 1.1;
    int& r_a = reinterpret_cast<int&>(b); // double引用 → int引用
    int a = 0x7FFF;
    int* p_a = reinterpret_cast<int*>(a); // 整数 → 指针

    使用场景

    • 任意指针/引用类型互转
    • 指针与整数的互转
      警告:易引发未定义行为,优先考虑 static_cast

隐式类型转换示例

cpp
class A {
public:
    A(int data) : m_data(data) {} // 允许隐式转换: int → A
    void print() { cout << "m_data = " << m_data << endl; }
private:
    int m_data;
};

int main() {
    A a = 10; // 隐式调用构造函数
    a.print(); // 输出 m_data = 10
}

禁止隐式转换:使用 explicit 关键字。

cpp
explicit A(int data) : m_data(data) {}
// A a = 10;   // 错误!必须显式转换
A a = A(10);  // 正确

1.2 异常

1.2.1 错误分类

  • 编译时错误:语法错误、类型不匹配等。
    cpp
    int a = 10;
    int p_a = &a; // 错误:int* 不能初始化 int
  • 运行时错误:执行时出现的错误(如除零、空指针访问)。
    cpp
    int a = 10 / 0;           // 除零错误
    int* p_a = NULL;
    cout << *p_a << endl;     // 空指针解引用

1.2.2 异常处理机制

异常处理三部分:

  1. 抛出异常 (throw)
    cpp
    if (error_condition) throw 异常对象;
  2. 捕获异常 (try-catch)
    cpp
    try { /* 可能抛出异常的代码 */ }
    catch (类型 形参) { /* 处理特定异常 */ }
    catch (...) { /* 处理所有未知异常 */ }
  3. 处理异常

示例:异常传递链

cpp
double func3(double num1, double num2) {
    const double rate = 1e-8;
    if (abs(num2) < rate) 
        throw string("除数为0"); // 抛出异常
    return num1 / num2;
}

double func2(double n1, double n2) { return func3(n1, n2); }
double func1(double n1, double n2) { return func2(n1, n2); }

int main() {
    try {
        double ret = func1(1.1, 0.0); // 异常向上传递
    }
    catch (string err) { 
        cout << err << endl; // 捕获并处理
    }
    cout << "over!" << endl;
    return 0;
}

执行流程

  1. func3 抛出异常 → func2func1main
  2. main 中匹配到 catch(string) 并处理
  3. 继续执行 catch 块后的代码

1.2.3 C++标准库异常类

  • runtime_error:运行时错误(如空指针)。
  • out_of_range:越界访问。
  • bad_alloc:内存分配失败。

共同接口
const char* what() const noexcept;

示例

cpp
// runtime_error
try {
    int* p = nullptr;
    if (!p) throw runtime_error("空指针");
    cout << *p << endl;
} catch (runtime_error e) {
    cout << e.what() << endl; // 输出:空指针
}

// out_of_range
int arr[5] = {1,2,3,4,5};
try {
    int idx = 5;
    if (idx >= 5) throw out_of_range("数组越界");
    cout << arr[idx] << endl;
} catch (out_of_range e) {
    cout << e.what() << endl;
}

// bad_alloc
try {
    int* p = new int[10000000000000000]; // 抛出 bad_alloc
} catch (bad_alloc& e) {
    cerr << "内存分配失败: " << e.what() << endl;
}

1.2.4 异常与类

  • 构造函数:可抛出异常(对象构造失败)。
  • 析构函数:禁止抛出异常(默认为 noexcept)。
  • 成员函数:可抛出异常。

noexcept 关键字

承诺函数不抛出异常。若违反,程序直接终止。
语法

cpp
返回值 函数名(参数) noexcept { /* 代码 */ }

示例

cpp
class A {
public:
    A() noexcept { 
        // throw -1; // 会导致程序崩溃
    }
    ~A() noexcept { 
        // throw -1; // 崩溃
    }
    void fun() noexcept {
        // throw -1; // 崩溃
    }
};

关键点总结

  • 类型转换:优先使用 C++ 风格转换(static_cast/dynamic_cast)。
  • 异常处理:通过 throw-try-catch 实现错误传递与处理。
  • 标准异常:使用 runtime_error/bad_alloc 等提高可读性。
  • 安全规范:析构函数禁止抛异常,noexcept 用于关键函数。

知识如风,常伴吾身