C++中的类型转换
C语言中的类型转换一般分为两种,显示类型转换与隐式类型转换
显示类型转换格式为:
原数据类型 变量名 = (目标数据类型)表达式
例:
double a = (int)a;
double a = 3.3,b = 4.5;
int c = (int)(a+b);
隐式类型转换则更为隐蔽,如
double c = 1.8;
int b = 4;
int a = b+c;//此时b与c相加,会隐式被转为浮点型,之后两者的相加之和则会隐式转化为整型;
在C++中则有四种常见的类型转换方式:静态类型转换,动态类型转换,常类型转换,重解释类型转换;
静态类型转换一般都是适用于基本类型之间的转换,静态转换在编译时进行,不提供运行时检查,使 用静态转换时需要注意类型之间的兼容性,否则可能导致未定义的行为。
静态类型转换格式:目标类型变量 = static_cast(源类型变量)
(void*)p;
int*p_a = static_cast<int*>(p);
动态类型转换用于在继承关系中进行类型转换。动态转换在运行时进行类型 检查,可以判断指针或引用是否指向了有效的派生类对象。如果转换失败,动态转换将返回空指针 (NULL)或抛出std::bad_cast异常。
动态类型转换格式:目标类型变量 = dynamic_cast(源类型变量)
在继承的内容中有一个概念叫向上造型,将子类类型的指针或引用转换成基类类型的指针或引用,动态转换可以应用于这个过程。
常类型转换用于去除常量属性,可以将const修饰的对象转换为非const。
int a = 10;
const int* p_a = &a;
*p_a = 11;//error 常量指针指向的值不能修改
int *p_b = const_cast<int *>(p_a);
*p_b = 11;
重解释类型:转换重新解释,可以用于任意类型之间的强制转换( 类似传统的类型转换 )
重解释类型转换格式:目标类型变量 = reinterpret_cast(源类型变量)
int b = 10;
double c = reinterpret_cast<int>(b);
异常处理
异常处理分为三个步骤:
抛出异常 throw
捕获异常 try-catch
处理异常
异常如果在当前函数被检测到,并不处理就会被抛到调用该函数的外侧函数,直到main函数,如果其还不处理整个程序就会异常中断。
在函数运行过程中会进行条件检测,如果发现异常,就会使用throw关键字,抛出异常,而编写程序的时候会将可能出现问题的代码写在try模块中,而将针对不同异常的异常处理方式写在catch块中,类似于switch;
当然C++中也有针对异常的标准库,C++标准库定义一组异常类,用于报告标准库函数遇到的问题,这些异常类型也可在程序中使用 runtime_error 只有在运行时才能检测出的问题
out_of_range 逻辑错误,范围越界
bad_alloc 内存分配失败
所有异常类型只定义了一个名为 what 的成员函数,该函数没有任何参数,返回一个C风格的字符串 const char*用于提供异常信息。
注意在类中,只有析构函数不能抛出异常,其实默认noexcept的,在函数后面写上noexcept关键字表示不会抛出异常。
#include <iostream>
#include <stdexcept>
#include <string>
// 自定义异常类
class MyCustomException : public std::exception {
private:
std::string message;
public:
MyCustomException(const std::string& msg) : message(msg) {}
const char* what() const noexcept override {
return message.c_str();
}
};
// 可能抛出异常的函数
double divideNumbers(double numerator, double denominator) {
if (denominator == 0) {
throw std::runtime_error("除数不能为零");
}
if (denominator == 1) {
throw MyCustomException("除数不能为1(自定义异常)");
}
return numerator / denominator;
}
int main() {
try {
std::cout << "请输入被除数和除数(用空格分隔): ";
double num1, num2;
std::cin >> num1 >> num2;
double result = divideNumbers(num1, num2);
std::cout << "结果是: " << result << std::endl;
// 测试自定义异常
// divideNumbers(10, 1);
}
catch (const std::runtime_error& e) {
std::cerr << "运行时错误: " << e.what() << std::endl;
}
catch (const MyCustomException& e) {
std::cerr << "自定义异常: " << e.what() << std::endl;
}
catch (const std::exception& e) {
std::cerr << "标准异常: " << e.what() << std::endl;
}
catch (...) {
std::cerr << "捕获到未知异常" << std::endl;
}
// 异常安全示例
try {
int* arr = new int[100]; // 可能抛出std::bad_alloc
// 使用数组...
delete[] arr;
}
catch (const std::bad_alloc& e) {
std::cerr << "内存分配失败: " << e.what() << std::endl;
}
return 0;
}
上面是一个异常处理的例子,其中需要注意的是
what(),这是每一个异常类都有的,其没有任何参数,主要作用就是为了返回一个从上图char*l类型的字符串用于返回异常信息。正如在自定义的异常类中被重写的what()函数,这里还有一个noexpect关键字,这就是前面所说的不会抛出异常的声明。