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
- 整数提升:当较小的整数类型(如char、short)参与运算时,会被隐式地转换为较大的整数类型(如int)。
函数参数传递:实参与形参类型不匹配时,编译器尝试隐式转换。
cppvoid 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种类型转换操作符:
静态类型转换 (static_cast)
基本类型转换,编译时检查。
语法:目标类型变量 = static_cast<目标类型>(源类型变量);
示例:cppchar c = 'a'; int a = static_cast<int>(c); // char → int void* p = &a; int* p_a = static_cast<int*>(p); // void* → int*使用场景:
- 基础类型转换(如 int → double)
- 指针类型转换(需兼容)
动态类型转换 (dynamic_cast)
用于继承关系的类型转换(运行时检查)。
语法:目标类型变量 = dynamic_cast<目标类型>(源类型变量);
要求:类必须有虚函数。
使用场景:基类与派生类指针/引用转换。常量类型转换 (const_cast)
移除const属性。
语法:目标类型变量 = const_cast<目标类型>(源类型变量);
示例:cppint 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属性。重解释类型转换 (reinterpret_cast)
任意类型强制转换(高风险)。
语法:目标类型变量 = reinterpret_cast<目标类型>(源类型变量);
示例:cppdouble 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 异常处理机制
异常处理三部分:
- 抛出异常 (throw)cpp
if (error_condition) throw 异常对象; - 捕获异常 (try-catch)cpp
try { /* 可能抛出异常的代码 */ } catch (类型 形参) { /* 处理特定异常 */ } catch (...) { /* 处理所有未知异常 */ } - 处理异常
示例:异常传递链
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;
}执行流程:
func3抛出异常 →func2→func1→mainmain中匹配到catch(string)并处理- 继续执行
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用于关键函数。
