文章目录
关于类型转换,通常是隐式转换或者强制转换,C++
提供了一些能够显式表示转换的运算符,能够更好的规避一些风险和错误
1.传统的类型转换
在 C
语言中,如果赋值运算符左右两侧类型不同,或者形参与实参类型不匹配,或者返回值类型与接收返回值类型不一致时,就需要发生类型转化
void Test()
{
int i = 1;
// 隐式类型转换
double d = i;
printf("%d, %.2f\n", i, d);
int* p = &i;
// 显示的强制类型转换
int address = (int)p;
printf("%x, %d\n", p, address);
}
转换分为显式和隐式:
- 隐式类型转化:编译器在编译阶段自动进行,能转就转,不能转就编译失败
- 显式类型转化:需要用户自己处理
然而这两种转换的前提是逻辑相近:
- 基本类型间的转换(如
int
↔double
) - 继承层级中的转换(如子类→父类、父类→子类)
- 用户定义的转换(如
operator T()
或带参数的构造函数)
string s;
vector<int> = (vector<int>) s;
这种转换就会失败,因为逻辑不相近
对于强制转换还有一种特殊易错的场景
int main()
{
const int n = 10;
int* p = (int*) &n;
(*p)++;
string s;
vector<int> = (vector<int>) s;
cout << n << endl;
cout << *p << endl;
return 0;
}
无论是有点 C
语言基础的人,甚至是学完 C++
的初学者,都很容易认为这里的输出结果是:n
为 11
,*p
为 11
。但是结果并非所愿,输出结果:n
为 10
,*p
为 11
实际上这里涉及到存储规则的问题,因为 n
是 const
变量,那么会被识别为一个常变量,可能会是一个经常被使用的值,就把 n
存入寄存器,把频繁使用的变量的值暂存到寄存器中,这样在后续对该变量的读取操作中,就不需要每次都去内存中读取,直接从寄存器中获取即可,因为寄存器的读写速度比内存快很多
通常 cout
输出的值都是在内存里读取的,相比寄存器,内存读写速度较慢,但内存容量相对寄存器大很多,大多的代码都是存在这里的
因此这里输出的 n
是取自内存器,*p
取自内存
volatile const int n = 10;
volatile
关键字表示该变量的值可能会在程序未明确指定的情况下被改变,编译器不会对其进行优化,即不会被存入寄存器,这样取到的 n
就是内存中及时更新的值
C
风格的转换格式很简单,但是有不少缺点的:
- 隐式类型转化有些情况下可能会出问题:比如数据精度丢失
- 显式类型转换将所有情况混合在一起,代码不够清晰
因此 C++
提出了自己的类型转化风格,注意因为 C++
要兼容 C
语言,所以 C++
中还可以使用 C
语言的转化风格
2.C++强制类型转换
2.1 static_cast
int main()
{
double d = 12.34;
int a = static_cast<int>(d);
cout << a << endl;
return 0;
}
static_cast
需要逻辑上的相近性
2.2 reinterpret_cast
int main()
{
// 这里使用static_cast会报错,应该使用reinterpret_cast
//int *p = static_cast<int*>(a);
int* p = reinterpret_cast<int*>(a);
return 0;
}
reinterpret_cast
几乎无类型限制,不要求类型相近,可强制转换任意指针或整数类型,但极其危险,可能导致:
- 违反别名规则(如通过
char*
修改int
) - 函数指针转换后调用,引发崩溃
- 平台依赖(如不同架构的指针大小不同)
2.3 const_cast
void Test()
{
volatile const int a = 2;
int* p = const_cast<int*>(&a);
*p = 3;
cout << a << endl;
}
仅改变类型的 const
/ volatile
属性,这里用 reinterpret_cast
也不行的,因为 const
的转换是有风险的
2.4 dynamic_cast
class A
{
public:
virtual void f()
{}
};
class B : public A
{};
void fun(A* pa)
{
// dynamic_cast会先检查是否能转换成功,能成功则转换,不能则返回
B* pb1 = static_cast<B*>(pa);
B* pb2 = dynamic_cast<B*>(pa);
cout << "pb1:" << pb1 << endl;
cout << "pb2:" << pb2 << endl;
}
int main()
{
A a;
B b;
fun(&a);
fun(&b);
return 0;
}
dynamic_cast
用于将一个父类对象的指针/引用转换为子类对象的指针或引用(动态转换)
- 向上转型: 子类对象指针/引用->父类指针/引用(不需要转换,赋值兼容规则)
- 向下转型: 父类对象指针/引用->子类指针/引用(用
dynamic_cast
转型是安全的)
pa
是指向子类对象 B
的,转换可以成功,正常返回地址;pa
是指向父类对象 A
的,转换失败,返回空指针
🔥值得注意的是: 必须是继承关系中的类,基类必须包含虚函数
3.RTTI
RTTI: Run-time Type identification
的简称,即:运行时类型识别
C++
通过以下方式来支持 RTTI
:
typeid
运算符dynamic_cast
运算符decltype