一、C语言中的类型转换
在 C 语言中,如果 赋值运算符左右两侧类型不同,或者形参与实参类型不匹配,或者返回值类型与 接收返回值类型不一致时,就需要发生类型转化 , C 语言中总共有两种形式的类型转换: 隐式类型 转换和显式类型转换 。
1. 隐式类型转化:编译器在编译阶段自动进行,能转就转,不能转就编译失败
2. 显式类型转化:需要用户自己处理
代码语言:javascript复制int main()
{
//隐式类型转换
int i = 5;
double d = i;
//显式类型转换
int* pi = &i;
int address = (int)pi;
return 0;
}
C风格的转换格式很简单,但是有不少缺点的:
1. 隐式类型转化有些情况下可能会出问题:比如数据精度丢失 2. 显式类型转换将所有情况混合在一起,代码不够清晰
因此 C 提出了自己的类型转化风格,注意 因为 C 要兼容 C 语言,所以 C 中还可以使用 C 语言的 转化风格 。
二、C 中的类型转换
标准 C 为了加强类型转换的可视性,引入了四种命名的强制类型转换操作符: static_cast、reinterpret_cast、const_cast、dynamic_cast
1.static_cast
static_cast用于非多态类型的转换(静态转换),编译器隐式执行的任何类型转换都可用static_cast,但它不能用于两个不相关的类型进行转换。
例如:
我们可以用于将 double 类型转化成int类型,但是不可以将 double *类型转化成int类型。
代码语言:javascript复制int main()
{
double d = 5.32;
int i = static_cast<int>(d);
double* pd = &d;
int di = static_cast<int>(pd);
cout << di << endl;
return 0;
}
2.reinterpret_cast
reinterpret_cast操作符通常为操作数的位模式提供较低层次的重新解释,用于将一种类型转换为另一种不同的类型。
例如:
我们上面无法将double*转化成int类型,用reinterpret_cast就可以实现。
代码语言:javascript复制int main()
{
double d = 5.32;
double* pd = &d;
int di = reinterpret_cast<int>(pd);
cout << di << endl;
return 0;
}
3.const_cast
const_cast最常用的用途就是删除变量的const属性,方便赋值
例如:
将const类型的常变量转化为没有const属性的类型
代码语言:javascript复制int main()
{
const int a = 2;
int* p = const_cast<int*> (&a);
*p = 3;
cout << a << endl;
cout << *p << endl;
return 0;
}
看到结果,很多人可能就会好奇了,怎么还会打印不同的结果呢?
这是因为,我们用const修饰a类型之后,编译器认为a之后都不会改变,就将a读取到寄存器中,之后读取a的数据都是用存储在寄存器中的数据。当我们用*p修改了a在内存中的值之后,打印a是打印寄存器中a的值,打印*p则是内存中a的值。所以结果会不同。
拓展:volatile
volatile是一个关键字,作用是保持内存的可见性,也就是每次读取数据都会从内存中读取。
代码语言:javascript复制int main()
{
volatile const int a = 2;
int* p = const_cast<int*> (&a);
*p = 3;
cout << a << endl;
cout << *p << endl;
return 0;
}
4.dynamic_cast
dynamic_cast用于将一个父类对象的指针/引用转换为子类对象的指针或引用(动态转换)
向上转型:子类对象指针 / 引用 -> 父类指针 / 引用 ( 不需要转换,赋值兼容规则 )
向下转型:父类对象指针 / 引用 -> 子类指针 / 引用 ( 用 dynamic_cast 转型是安全的 )
注意:
1. dynamic_cast 只能用于父类含有虚函数的类(如果是子类含有虚函数而父类不具有,那么无法转换)
2. dynamic_cast 会先检查是否能转换成功,能成功则转换,不能则返回 0
代码示例:
当我们用强制类型转换的时候,是可能会发生越界问题的。
代码语言:javascript复制class Father
{
public:
virtual void f(){}
private:
int _a;
};
class Child :public Father
{
public:
int _b = 2 ;
};
void Func(Father* f)
{
Child* ch = (Child*)f;
cout << ch->_b << endl;
}
int main()
{
Father* th = new Father;
Child* ch = new Child;
Func(th);
Func(ch);
return 0;
}
我们再用dynamic_cast来将父类转换成子类
代码语言:javascript复制class Father
{
public:
virtual void f(){}
private:
int _a;
};
class Child :public Father
{
public:
int _b = 2 ;
};
void Func(Father* f)
{
Child* ch = dynamic_cast<Child*>(f);
if (ch == 0)
{
cout << "转换失败" << endl;
return;
}
cout << ch->_b << endl;
}
int main()
{
Father* th = new Father;
Child* ch = new Child;
Func(th);
Func(ch);
return 0;
}
我们可以看到,会出现越界问题的就会转化失败返回0。
这样使得我们的程序更加安全。
三、RTTI
RTTI : Run-time Type identifification 的简称,即:运行时类型识别。
C 通过以下方式来支持 RTTI :
1. typeid运算符
2. dynamic_cast运算符
3. decltype
1.typeid运算符
作用:一般我们使用typeid来查看变量的类型。
格式:typeid(变量).name()
代码示例:
代码语言:javascript复制int main()
{
int a = 0;
cout << typeid(a).name() << endl;
return 0;
}
2. decltype
作用:复制变量的类型作为使用。
格式:decltype(变量) 新变量名
代码语言:javascript复制int main()
{
int a = 0;
decltype(a) b = 10;
cout << b << endl;
return 0;
}
我们有时候可能会忘记了某个变量的名字,但是查找定义又十分麻烦,为了便于创建一个同类型的变量,我们可以使用decltype。