C++ 类型转换与异常处理全解析

一、C++ 类型转换

C++ 提供了 4 种显式类型转换方式,分别适用于不同场景,相比 C 语言的隐式转换更安全、可读性更高。

1. 静态类型转换(static_cast)

核心用途:基本数据类型转换、有继承关系的类指针 / 引用转换(编译期检查)。

(1)基本数据类型转换
cpp 复制代码
void test01() {
	char a = 'a ';
	//char->double;
	//static_cast<要转到的类型>(将谁转换)
	double d = static_cast<double> (a);
	double d1 = (double)a;
}
(2)派生类与基类指针转换
复制代码
cpp 复制代码
class Father{

};
class Son :public Father {

};
class Other {
};
void test02() {
	Father* f = NULL;
	Son* s = NULL;
	//Son s1=static_cast<Son*>(f);向下转化不安全
	Father* f1 = static_cast<Father*> (s);
	//Other* o = static_cast<Other*> (s);没有继承关系的类不能转化

}
(3)引用转换
复制代码
cpp 复制代码
void test03() {
    Father f;
    Son s;
    Father& ref_f = f;
    Son& ref_s = s;
    // 子类引用转父类引用
    static_cast<Father&>(ref_s);
}

}

2. 动态类型转换(dynamic_cast)

核心用途:多态类(含虚函数)的指针 / 引用转换(运行期检查),仅支持有继承关系的类。

复制代码
cpp 复制代码
// 基础类型不能用dynamic_cast
// void test04() {
//     char a = 'a';
//     dynamic_cast<double>(a); // 编译报错
// }

class Father02 {
public: 
    virtual void test() {}; // 虚函数,开启多态
};
class Son02 :public Father02 {
public:
    virtual void test() {};
};

void test05() {
    Father02* f = new Son02; // 父类指针指向子类对象(多态)
    // 向下转换安全,运行期检查类型
    Son02* s = dynamic_cast<Son02*>(f);
}

3. 常量类型转换(const_cast)

核心用途 :移除 / 添加变量的const属性,仅针对指针 / 引用。

cpp 复制代码
void test06() {
	const int* p = NULL;
	//const->不带const
	int* newP = const_cast<int*>(p);
	int* pp = NULL;
	const int* newPP = const_cast<const int*>(pp);
}

4. 重新解释转换(reinterpret_cast)

核心用途:任意类型间的二进制位重新解释(极其不安全,慎用)。

cpp 复制代码
void test07() {
    int a = 10;
    // 整数转指针
    int* p = reinterpret_cast<int*>(a);
    
    Father* f = NULL;
    // 无继承关系的类指针转换
    Other* o = reinterpret_cast<Other*>(f);
}

二、C++ 异常处理详解

C 语言通过返回值处理错误存在诸多缺陷,C++ 的异常机制可更优雅地处理程序运行时错误。

1. 为什么需要异常?

C 语言返回值处理错误的问题:

  • 返回值语义不明确(比如 - 1 可能代表多种错误);

  • 仅能返回单一信息,无法携带详细错误描述;

  • 返回值可被忽略,导致错误未被处理。

cpp 复制代码
// C风格错误处理示例
int printArray(int arr[], int len) {
    if (arr == NULL) return -1; // 空指针错误
    if (len == 0) return 0;    // 空数组错误
    return 1;
}

void test() {
    int* arr = NULL;
    int len = 0;
    printArray(arr, len); // 忽略返回值,错误未处理
}

2. 异常基础语法

异常处理三要素:try(监控可能出错的代码)、throw(抛出异常)、catch(捕获并处理异常)。

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

int func(int a, int b) {
    if (b == 0) {
        // 抛出int类型异常
        throw 10;
    }
    return a / b;
}

void test() {
    int a = 10;
    int b = 0;
    // 监控异常
    try {
        func(a, b);
        cout << "异常抛出后,此行不会执行" << endl;
    }
    // 捕获int类型异常
    catch (int) {
        cout << "除数不能为0" << endl;
    }
}

3. 异常的优势

(1)携带丰富的错误信息(抛出对象)
cpp 复制代码
class ErrorInfo {
public:
    void printError() {
        cout << "错误详情:除数不能为0" << endl;
    }
};

int func(int a, int b) {
    if (b == 0) {
        ErrorInfo err;
        throw err; // 抛出对象,携带更多信息
    }
    return a / b;
}

void test() {
    try {
        func(10, 0);
    }
    catch (ErrorInfo err) {
        err.printError();
    }
}
(2)异常无法忽略(忽略则程序崩溃)

若未捕获异常,程序会直接终止,强制开发者处理错误(相比返回值更严格)。

(3)异常可向上传递
cpp 复制代码
void test() {
    try {
        func(10, 0);
    }
    catch (double s) {
        // 不处理,向上抛给调用者
        throw;
    }
}

int main() {
    try {
        test();
    }
    catch (double d) {
        cout << "main函数捕获异常:" << d << endl;
    }
    return 0;
}

4. 异常的高级特性

(1)严格类型匹配 & 万能捕获

异常捕获需严格匹配类型,catch(...)可捕获任意类型异常(兜底处理)。

cpp 复制代码
void func(int a, int b) {
    if (b == 0) {
        throw 'a'; // 抛出char类型异常
    }
}

void test() {
    try {
        func(10, 0);
    }
    // 精准捕获
    // catch (int) {}
    // 万能捕获
    catch (...) {
        cout << "捕获未知类型异常" << endl;
    }
}
(2)异常接口声明

限制函数可抛出的异常类型(Qt 等框架常用):

cpp 复制代码
// 仅允许抛出int/char类型异常
void func() throw(int, char) {
    throw 10.0; // 抛出double异常,框架会拦截
}
(3)栈解旋

抛出异常后,函数栈上的局部对象会被自动析构(释放资源):

cpp 复制代码
class Maker {
public:
    Maker() { cout << "Maker构造" << endl; }
    ~Maker() { cout << "Maker析构" << endl; }
};

void func() {
    Maker m; // 栈上对象
    throw m; // 抛出异常,m会被析构(栈解旋)
    cout << "此行不会执行" << endl;
}

void test() {
    try {
        func();
    }
    catch (Maker) {
        cout << "捕获异常" << endl;
    }
}
(4)异常对象的生命周期

推荐使用引用捕获匿名对象,减少拷贝,仅生成 1 个对象:

复制代码
cpp 复制代码
// 最优方式:仅生成1个对象
void func() {
    throw Maker(); // 匿名对象
}

void test() {
    try {
        func();
    }
    catch (Maker& m) { // 引用捕获,无拷贝
        cout << "捕获异常" << endl;
    }
}
(5)异常的多态应用

通过基类捕获子类异常,统一处理不同类型错误:

cpp 复制代码
// 异常基类
class BaseError {
public:
    virtual void printError() = 0; // 纯虚函数
};

// 空指针异常
class NullError : public BaseError {
public:
    virtual void printError() {
        cout << "空指针异常" << endl;
    }
};

// 越界异常
class OutError : public BaseError {
public:
    virtual void printError() {
        cout << "数组越界异常" << endl;
    }
};

void func(int a) {
    if (a == 0) throw NullError();
    if (a > 100) throw OutError();
}

void test() {
    try {
        func(200);
    }
    catch (BaseError& err) { // 基类引用捕获子类
        err.printError(); // 多态调用
    }
}

三、总结

  1. 类型转换:static_cast(编译期,通用)、dynamic_cast(运行期,多态类)、const_cast(修改 const)、reinterpret_cast(二进制重解释,慎用);

  2. 异常处理:通过try-catch-throw处理错误,相比返回值更严格、信息更丰富;

  3. 最佳实践:异常对象用引用捕获、利用多态统一处理异常、避免滥用reinterpret_cast

相关推荐
ym_xixi1 小时前
《类和对象》—— 构造函数与析构函数总结
前端·c++·算法
luck_bor1 小时前
使用接口定义规范,实现类完成具体逻辑
java·开发语言
凯瑟琳.奥古斯特1 小时前
丑数II C++三指针解法(力扣264)
数据结构·c++·算法·leetcode·职场和发展
YYYing.1 小时前
【C++项目之高并发内存池 (四)】三层缓存的空间回收流程详解
c++·笔记·缓存·高并发·内存池
小小de风呀1 小时前
de风——【从零开始学C++】(六):模板初阶
开发语言·c++
likerhood1 小时前
java的泛型(generics)详细讲解
java·开发语言
j_xxx404_1 小时前
力扣算法:用栈消消乐,巧解相邻重复与退格字符串
c++·算法·leetcode
知识分享小能手1 小时前
R语言入门学习教程,从入门到精通,R语言流程控制语句(5)
开发语言·学习·r语言
大龄码农-涵哥1 小时前
Java 调用 LLM 全解析:ChatGPT、Claude、通义千问一网打尽
java·开发语言·chatgpt