【重走C++学习之路】26、类型转换
目录
一、C语言中的类型转换
二、C++中的四个类型转换
2.1 static_cast
2.2 dynamic_cast
2.3 const_cast
2.4 reinterpret_cast
2.5 总结
结语
一、C语言中的类型转换
在C语言中,如果赋值运算符左右两侧类型不同,或者形参与实参类型不匹配,或者返回值类型与接收返回值类型不一致时,就需要发生类型转化,C语言中总共有两种形式的类型转换:隐式类型转换 和 显式类型转换。
隐式类型转换(截断或提升):
编译器在编译阶段自动进行,能转就转,不能转就编译失败
double pi = 3.14159;
int truncatedPi = pi; // 隐式转换,丢失小数部分
printf("Pi: %d\n", truncatedPi); // 输出:3
return 0;
隐式转换可能导致数据丢失:在隐式类型转换中,可能会将一个较大范围的数据类型转换为一个较小范围的类型,导致数据丢失或精度损失。例如,
double
转换为int
,会丢失小数部分。
显式类型转换(强转):
用户自己手动强制转换变量类型
int num = 100;
int *int_ptr = #
void *void_ptr = (void*)int_ptr; // 将int指针转换为void指针
int *new_int_ptr = (int*)void_ptr; // 再将void指针转换回int指针
常见场景:
- 数值类型转换
- 指针类型转换
- 函数参数类型适配
注意:
- 数据截断风险
在把大范围的数据类型转换为小范围的数据类型(例如从long
转换为int
)时,可能会导致数据截断。- 浮点精度丢失
将double
类型转换为float
类型时,可能会造成精度的损失。- 指针转换的危险性
错误的指针类型转换可能会使程序崩溃或者产生未定义行为。
二、C++中的四个类型转换
C风格的转换格式很简单,但是有不少缺点的:
1. 隐式类型转化有些情况下可能会出问题:比如数据精度丢失
2. 显式类型转换将所有情况混合在一起,代码不够清晰
因此C++提出了自己的类型转换风格,但C++中仍然可以使用C的类型转换。
在 C++ 里,为了进行类型转换,提供了四个功能不同的类型转换运算符,分别是static_cast
、dynamic_cast
、const_cast
以及reinterpret_cast
。
2.1 static_cast
static_cast主要用于进行良性转换,非多态类型的转换(静态转换),编译器隐式执行的任何类型转换都可用static_cast,但它不能用于两个不相关的类型进行转换。它在编译阶段就会完成类型检查。
使用格式:
static_cast<new_type>(expression)
使用场景:
- 基本数据类型之间的转换
double d = 3.14;
int i = static_cast<int>(d); // 把double类型转换为int类型,值为3
- 子类到父类的向上转型
class Base {};
class Derived : public Base {};Derived d;
Base* b = static_cast<Base*>(&d); // 安全的向上转型
- 显式调用转换构造函数或者转换运算符
class Fraction
{
public:Fraction(int num, int den = 1) : numerator(num), denominator(den) {}operator double() const { return (double)numerator / denominator; }
private:int numerator;int denominator;
};Fraction f(3, 4);
double d = static_cast<double>(f); // 调用operator double()
2.2 dynamic_cast
dynamic_cast
主要用于进行安全的向下转型(将父类指针或引用转换为子类指针或引用),它会在运行时进行类型检查。如果转换不安全,对于指针会返回nullptr
,对于引用则会抛出std::bad_cast
异常。
使用格式:
dynamic_cast<new_type>(expression)
使用场景:
- 安全的向下转型
class Base { virtual void dummy() {} }; // 必须有虚函数,才能进行RTTI
class Derived : public Base {};Base* b = new Derived;
Derived* d = dynamic_cast<Derived*>(b); // 转换成功,d不为nullptr
- 交叉转型
class Base { virtual void dummy() {} };
class Derived1 : public Base {};
class Derived2 : public Base {};
class Derived3 : public Derived1, public Derived2 {};Derived3* d3 = new Derived3;
Derived1* d1 = dynamic_cast<Derived1*>(d3); // 转换成功
Derived2* d2 = dynamic_cast<Derived2*>(d1); // 交叉转型,d2不为nullptr
2.3 const_cast
const_cast
专门用于修改类型的const
或volatile
属性,主要用于去除const
限定(但要注意,通过这种方式修改原本为const
的对象是未定义行为)。
使用格式:
const_cast<new_type>(expression)
使用场景:
- 去除函数参数的
const
属性
void print(char* str)
{printf("%s\n", str);
}const char* cstr = "Hello";
char* str = const_cast<char*>(cstr); // 去除const限定
// print(cstr); // 错误:不能将const char*传递给char*
print(str); // 正确
- 在
const
成员函数中修改对象状态
class Counter
{
public:void increment() const {// count++; // 错误:在const成员函数中不能修改成员变量const_cast<int&>(count)++; // 正确:去除const限定}
private:int count;
};
2.4 reinterpret_cast
reinterpret_cast
用于进行低级的类型重新解释,比如将指针转换为整数,或者将一种类型的指针转换为另一种不相关类型的指针。
使用格式:
reinterpret_cast<new_type>(expression)
使用场景:
- 指针和整数之间的转换
int* ptr = new int(10);
uintptr_t addr = reinterpret_cast<uintptr_t>(ptr); // 将指针转换为整数
int* new_ptr = reinterpret_cast<int*>(addr); // 将整数转换回指针
- 不相关类型指针之间的转换
int num = 10;
int* int_ptr = #
char* char_ptr = reinterpret_cast<char*>(int_ptr); // 将int指针转换为char指针
- 函数指针类型的转换
typedef void (*FuncPtr)();int add(int a, int b)
{ return a + b;
}FuncPtr func = reinterpret_cast<FuncPtr>(add); // 函数指针类型转换
// func(); // 调用会导致未定义行为
2.5 总结
转换运算符 | 主要用途 | 安全性 |
---|---|---|
static_cast | 基本类型转换、向上 / 向下转型(不进行运行时检查) | 中等 |
dynamic_cast | 安全的向下转型、交叉转型(运行时类型检查) | 高 |
const_cast | 修改const /volatile 属性 | 低(容易导致未定义行为) |
reinterpret_cast | 低级指针类型重新解释、指针与整数转换 | 极低(非常危险) |
结语
在实际编程中,优先使用C++风格的类型转换,避免使用C风格的强制类型转换,提高代码的安全性和可读性,这种用法会在Linux编程中见到。
下一篇将会介绍C++中IO流相关的知识,可以看作文件操作,有兴趣的朋友可以关注一下。