1. C语言中的类型转换
在C语言中,如果赋值运算符左右两侧类型不同,或者形参与实参类型不匹配,或者返回值类型与接收返回值类型不一致时,就需要发生类型转化,C语言中总共有两种形式的类型转换:隐式类型转换和显式类型转换。
- 隐式类型转化:编译器在编译阶段自动进行,能转就转,不能转就编译失败
- 显式类型转化:需要用户自己处理
注意:C语言
- 隐式类型转换:整形之间 浮点数和整形之间
- 强制类型转换:指针之间 整形和指针
- 无关联类型是不支持转换的
2.C++中的类型转换
兼容C的转换用法:
- 内置类型 -》 自定义类型
- 自定义类型 -》 内置类型
- 自定义类型 -》 自定义类型
class A
{
public:
A(int a1)
:_a1(a1)
{}
A(int a1, int a2)
:_a1(a1)
, _a2(a2)
{}
operator int()
{
return _a1 + _a2;
}
int get() const
{
return _a1 + _a2;
}
private:
int _a1 = 1;
int _a2 = 1;
};
class B
{
public:
B(const A& aa)
:_b1(aa.get())
{}
private:
int _b1;
};
int main()
{
int i = 1;
// 隐式类型转换
double d = i;
printf("%d, %.2f\n", i, d);
int* p = &i;
// 显示的强制类型转换
int address = (int)p;
printf("%p, %d\n", p, address);
// 类型强制转换”: 无法从“int *”转换为“double”
// double x = (double)p;
// 内置类型 -> 自定义类型
A aa1 = 1;
// C++11
A aa2 = {2,2};
// 自定义类型 -> 内置类型
int x = aa1;
cout << x << endl;
// 自定义类型 -> 自定义类型
B bb = aa1;
//while (cin>>x)
//while (cin.operator>>(x))
// 输入字符则就跳出循环
// operator bool判断是否输入的类型正确
while (cin.operator>>(x).operator bool())
{
cout << x << endl;
}
// 直接输入字符串
string str;
while (cin>>str)
{
cout << str << endl;
}
return 0;
}
为什么C++需要四种类型转换?
C风格的转换格式很简单,但是有不少缺点的:
- 隐式类型转化有些情况下可能会出问题:比如数据精度丢失
- 显式类型转换将所有情况混合在一起,代码不够清晰
因此C++提出了自己的类型转化风格,注意因为C++要兼容C语言,所以C++中还可以使用C语言的
转化风格
隐式类型转换的坑
// 隐式类型转换的坑
void Insert(size_t pos)
{
int end = 10;
while (end >= pos)
{
cout << end << endl;
--end;
}
}
int main()
{
Insert(5);
Insert(0);
return 0;
}
由于参数是无符号整数,进行比较时会进行隐式类型转换,转换为无符号整数进行比较,根据底层,无符号整数小于0时,底层当作负数,按位取反+1,但是实质上还是无符号的整数去比较,则之后会将这个数据转化为正整数,则这个数会很大,所以会一直在循环中,之后会不断打印出来负数。
3. C++强制类型转换
标准C++为了加强类型转换的可视性,引入了四种命名的强制类型转换操作符:
static_cast、reinterpret_cast、const_cast、dynamic_cast
3.1 static_cast
static_cast用于非多态类型的转换(静态转换),编译器隐式执行的任何类型转换都可用
static_cast,但它不能用于两个不相关的类型进行转换
static_cast:隐式类型转换(静态)
int main()
{
int i = 1;
// 隐式类型转换 : static_cast
double d = static_cast<double>(i);
printf("%d, %.2f\n", i, d);
int* p = &i;
return 0;
}
3.2 reinterpret_cast
reinterpret_cast操作符通常为操作数的位模式提供较低层次的重新解释,用于将一种类型转换为另一种不同的类型。
(显式)强制类型转换
int main()
{
int i = 1;
// 隐式类型转换 : static_cast
double d = static_cast<double>(i);
printf("%d, %.2f\n", i, d);
int* p = &i;
// 显示的强制类型转换 : reinterpret_cast
int address = reinterpret_cast<int>(p);
// error C2440: “static_cast”: 无法从“int *”转换为“int”
// int address1 = static_cast<int>(p);
printf("%p, %d\n", p, address);
// 类型强制转换”: 无法从“int *”转换为“double”
// double x = (double)p;
return 0;
}
3.3 const_cast
const_cast最常用的用途就是删除变量的const属性,方便赋值。
通常const修饰的数据是不可以被改变的,即使用上述方法进行了修改,但是输出的值还是没有修改以前的,就如,a已经通过指针进行了修改,但是输出a还是没有变化。
int main()
{
volatile const int a = 1;
// a++;
int x = 0;
cout << &a << endl;
cout << &x << endl;
//int* ptr = (int*)&a;
int* ptr = const_cast<int*>(&a);
(*ptr)++;
cout << *ptr << endl;
cout << a << endl;
return 0;
}
所以增加const_cast:从稳定变量变成可变变量,const属性已经去掉,a的值也就发生了改变。用const_cast时在前面的数据上加volatile,来保持稳定。
3.4 dynamic_cast
dynamic_cast用于将一个父类对象的指针/引用转换为子类对象的指针或引用(动态转换)
向上转型:子类对象指针/引用->父类指针/引用(不需要转换,赋值兼容规则)
向下转型:父类对象指针/引用->子类指针/引用(用dynamic_cast转型是安全的)
注意:
- dynamic_cast只能用于父类含有虚函数的类
- dynamic_cast会先检查是否能转换成功,能成功则转换,不能则返回0
// dynamic_cast
class A
{
public:
virtual void f() {}
int _a1 = 1;
};
class B : public A
{
public:
int _b1 = 1;
};
void fun(A* pa)
{
// pa指向B对象,转换成功
// pa指向A对象,转换失败,返回空
B* pb = dynamic_cast<B*>(pa);
if (pb)
{
cout << pb << endl;
pb->_b1++;
}
else
{
cout << "转换失败" << endl;
}
}
int main()
{
A a;
B b;
fun(&a);
fun(&b);
return 0;
}
注意
强制类型转换关闭或挂起了正常的类型检查,每次使用强制类型转换前,程序员应该仔细考虑是否还有其他不同的方法达到同一目的,如果非强制类型转换不可,则应限制强制转换值的作用域,以减少发生错误的机会。强烈建议:避免使用强制类型转换
4. RTTI(了解)
RTTI:Run-time Type identification的简称,即:运行时类型识别。
C++通过以下方式来支持RTTI:
- typeid运算符
- dynamic_cast运算符
- decltype
5. 常见面试题
- C++中的4中类型转化分别是:static_cast、reinterpret_cast、const_cast、dynamic_cast__
- 说说4中类型转化的应用场景。