- C语言的类型转换
- 整型提升
- 为什么C++需要引入四种类型转换
- C++11 四种类型转换
- 1.相关类型的转换-static_cast
- 2.不相关类型转换-reinterpret_cast
- 3.去掉const属性类型转换-const_cast
- 4. 动态转换--dynamic_cast
- 总结
- 常见面试题
在C语言中,如果赋值运算符左右两侧类型不同,或者形参与实参类型不匹配,或者返回值类型与接收返回值 类型不一致时,就需要发生类型转化,C语言中总共有两种形式的类型转换:隐式类型转换和显式类型转换。
- 隐式类型转化:编译器在编译阶段自动进行,能转就转,不能转就编译失败;
- 显式类型转化:需要用户自己处理;
void Test()
{
int i = 1;
// 隐式类型转换 -- 相近类型/相关类型 -- 意义相似,代表数字;
double d = i;
printf("%d, %.2fn", i, d);
int* p = &i;
// 显示的强制类型转换 -- 不相近类型/不相关类型,强制转换!
int address = (int)p;
printf("%x, %dn", p, address);
}
整型提升
void Test()
{
size_t pos;
cin >> pos; // 0
int n = 5;
while (n >= pos) // 整形提升,本质就是隐式类型转换
{
cout << n << endl;
--n;
}
}
当输入pos==0时,这个程序进入死循环,原因是当size_t n 和 int pos做比较(>=)时,编译器会隐式地进行整形提升,将较小范围的int提升成较大范围的size_t再进行比较;
因此n减到负数以后虽然
缺陷: 转换的可视性比较差,所有的转换形式都是以一种相同形式书写,难以跟踪错误的转换
可见,若编写程序不注意,这样的隐式类型转换容易让程序出错;
为什么C++需要引入四种类型转换C风格的转换格式很简单,但是有不少缺点的:
- 隐式类型转化有些情况下可能会出问题:比如数据精度丢失;
- 显式类型转换将所有情况混合在一起,代码不够清晰;
因此C++提出了自己的类型转化风格,注意因为C++要兼容C语言,所以C++中还可以使用C语言的转化风格。
C++11 四种类型转换标准C++为了加强类型转换的可视性,引入了四种命名的强制类型转换操作符:
1.相关类型的转换-static_caststatic_cast用于非多态类型的转换(静态转换),编译器隐式执行的任何类型转换都可用static_cast,但它 不能用于两个不相关的类型进行转换
int main()
{
double d = 12.34;
int a = static_cast(d);//相近类型/相关类型 -- 意义相似
cout << a << endl;
return 0;
}
2.不相关类型转换-reinterpret_cast
reinterpret_cast操作符通常为操作数的位模式提供较低层次的重新解释,用于将一种类型转换为另一种不 同的类型
int main()
{
int i = 0;
int* p = &i;
//int address = static_cast(p); // 报错(因为int和int*类型不相关)
// 显示的强制类型转换
int address = reinterpret_cast(p);
cout << address << endl << p << endl;
return 0;
}
3.去掉const属性类型转换-const_cast
const_cast最常用的用途就是删除变量的const属性,方便赋值
int main()
{
const int a = 2;
//a = 3;
int* p = const_cast(&a); // &a的类型:const int*
*p = 3;
cout << a << endl; // 输出 2
cout << *p << endl; // 输出 3
return 0;
}
经过调试会发现a对应空间里的值其实已经通过const_cast去const属性后用p指针改为了3,只是编译器优化认为a是const属性里面的值不会变,所以cout读取的是开始保存在寄存器里的备份2,并非a对应空间里面目前存放的真值3;
添加voliate关键字解决:
int main()
{
volatile const int a = 2;//voliate--隐含易变的意思,表明每次读取a的值需要从其空间取出,不用之前寄存器放的备份,保证读取正确的值;
//a = 3;
int* p = const_cast(&a); // &a的类型:const int*
*p = 3;
cout << a << endl; // 输出 2
cout << *p << endl; // 输出 3
return 0;
}
4. 动态转换–dynamic_castvolatile 关键字告诉编译器 变量a 是随时可能发生变化的,每次使用它的时候必须从内存中取出a的值,因而编译器生成的汇编代码会重新从 a 的地址处读取数据。这样看来,如果 a 是一个寄存器变量或者表示一个端口数据或者是多个线程的共享数据,就容易出错,所以说volatile 可以保证对特殊地址的稳定访问。
dynamic_cast用于将一个父类对象的指针/引用转换为子类对象的指针或引用(动态转换)
向上转型:子类对象指针/引用->父类指针/引用(不需要转换,赋值兼容规则(切片原理))
向下转型:父类对象指针/引用- >子类指针/引用(用dynamic_cast转型是安全的,可以规避不安全情况;否则可能不安全)
注意: 1. dynamic_cast只能用于含有虚函数的类 2. dynamic_cast会先检查是否能转换成功,能成功则转换,不能则返回0
不使用
class A
{
public:
virtual void f() {}//dynamic_cast只能用于含有虚函数的类
private:
int _a = 1;
};
class B : public A
{
public:
int _b = 1;
};
void fun(A* pa)
{
B* ptr =(B*)pa;//C的强制转换,直接转换指针;
ptr->_b = 10;
cout << ptr << endl;
}
int main()
{
A a;
B b;
fun(&a);//不安全 fun中的A*是基类a对象传入,那么这个A* pa指针就没有权限访问派生类的_b成员,报错;
fun(&b);//安全 fun中的A*指针是派生类b对象传过去的,进行切片了但是下文数据还在,输出_b;
return 0;
}
正确使用
void fun(A* pa)
{
B* ptr = dynamic_cast(pa);
//使用dynamic_cast动态转换可以在下面的if中将规避掉不安全的内存访问情况;
if (ptr)
{
ptr->_b = 10;
cout << ptr << endl;
}
else
{
cout << "转换失败" << endl;
}
}
int main()
{
A a;
B b;
fun(&a);//不安全 fun中的A*是基类a对象传入,那么这个A* pa指针就没有权限访问派生类的_b成员;
fun(&b);//安全 fun中的A*指针是派生类b对象传过去的,进行切片了但是下文数据还在,A* pa可以访问_b成员;
return 0;
}
即:
- pa如果是指向父类对象(指针引用),严格来说是不能转的,因为存在风险,访问时存在越界风险;
- pa如果是指向子类对象(指针引用),可以转换,是安全的(切片技术);
- 如果使用c的强制类型转换,那么这里是不安全的,无法识别上面的两种情况;
- 建议使用dynamic_cast,则他是安全的。如果是第2种情况可以转换成功,就算是第1种非法情况会认定转换失败,返回nullptr给指针,后面程序会if判断出来,不会崩溃;
标准C++引入了四种命名的强制类型转换操作符,加强了类型转换的可视性,使代码更清晰,使得即便出错C语言中那种隐式错误也能够及时发现;
常见面试题
- C++中的4中类型转化分别是:_,_,_,_
- 说说4中类型转化的应用场景。



