目录
一. C语言的类型转换
二. C++类型转换
2.1 static_cast
2.2 reinterpret_cast
2.3 const_cast
2.4 dynamic_cast
三. 运行时类型识别 -- RTTI
四. 总结
一. C语言的类型转换
C语言的类型转换分为隐式类型转换和强制类型转换,隐式类型转换发生在相近的类型之间,强制类型转换可以完成不相关类型之间的转换。
- 隐式类型转换:当运算符两侧的数据类型不同时,其中一个数据的类型会被隐式转换为另一个数据的类型,转换规则遵循图1.1中自上而下的顺序。
- 强制类型转换:实现不相关类型之间的转换,如:将int型数据转换为int*类型的数据。编译器不会自动进行强制类型转换,需要用户显示的去进行转换。注意:并不是任何不相关的数据都能进行强制类型转换,如自定义类型和内置类型之间不能强转。
C语言的类型转换有以下缺陷:
- 隐式类型转换,有可能造成精度的丢失。
- 将所有强制类型转换混在一起,不易区分。
为了弥补C语言类型转换的缺陷,C++提出了属于自己的四种类型转换方式,但是C++兼容C语言,C语言的类型转换规则在C++中依旧适用。
二. C++类型转换
C++有static_cast、interpret_cast、const_cast、_dynamic四种类型转换方式。
2.1 static_cast
用法:static_cast<转换后的类型>(被转换类型的变量)
功能:用于执行任何C语言中的隐式类型转换,不可以用于非相关类型的强制类型转换。
int main()
{
double d1 = 12.1234;
int i1 = static_cast<int>(d1); //将d1的类型转换为int
int i2 = 3;
double d2 = static_cast<double>(i2); //将i2的类型
int a = 1234567;
//int* pa = static_cast<int*>(a); //无法实现不相关类型之间的强制类型转换
return 0;
}
2.2 reinterpret_cast
用法:reinterpret_cast<转换后的类型>(被转换类型的变量)
功能:指向不相关类型之前的强制类型转换,不可用于相关类型之间的隐式类型转换
注意:如果被转换类型的对象具有const属性,那么使用reinterpret_cast不能取消其const属性,如:const int b = 10; int* pb = reinterpret_cast<int*>(&b)就无法通过编译,因为&b本身的类型为const int*, reinterpret_cast<int*>(&b)将其const属性取消了。
但是,使用C语言的强制类型转换(int*)&b能够取消const属性。
int main()
{
int a = 1234567;
int* pa = reinterpret_cast<int*>(a);
std::cout << pa << std::endl;
double d = 10.11;
//int i = reinterpret_cast<int>(d); //不能进行相近类型之间的转换
const int b = 10;
//int* pb1 = reinterpret_cast<int*>(&b); //不能取消&b的const属性
int* pb2 = (int*)&b; //编译通过
*pb2 = 20;
return 0;
}
2.3 const_cast
用法:const_cast<不具有const属性的类型>(具有const属性的对象)
功能:取消原本对象/变量类型的const属性
如:const int a = 10,不能直接对a的值进行修改,但是,如果通过const_cast操作符,拿到不具有const属性的a的地址,就可以通过解引用改变a的值。
使用reinterpret_cast无法取消const属性。
int main()
{
const int a = 1;
int* pa = const_cast<int*>(&a);
*pa = 2;
return 0;
}
2.4 dynamic_cast
用法:dynamic_cast<子类指针或引用类型>(父类指针或引用)
功能:在保证安全的前提下,将父类的指针或引用类型转换为子类的指针或引用类型
- 正常情况下,派生类对象可以赋值给基类对象,基类的对象一定不能赋值给派生类的对象。派生类的指针或引用可以赋值给基类的指针或引用。
- 基类的指针或引用经过强制类型转换后,可以赋值给派生类的指针或引用,但这样并不安全,容易出现越界访问的问题。
- dynamic_cast可以用于将基类对象的引用或指针,经强制类型转换后赋值给派生类的引用或指针。使用dynamic_cast要么类型转换失败,如果成功就一定安全。
关于dynamic_cast强制类型转换,有以下两点注意事项:
- 要求基类必须有虚函数,否则无法通过dynamic_cast实现类型转换。
- 如果强制将基类的引用转换为派生类的引用时失败,dynamic_cast会抛出异常,如果强制将基类的指针转换为派生类的指针时失败,dynamic_cast返回空指针nullptr。
一般来说,如果某个基类的引用或指针,实际指向某个派生类对象,dynamic_cast就会强转成功,如果基类的引用或指针就是指向某个基类对象而不是派生类对象,强转类型转换就会失败。
如下面的代码,定义了两个基类指针pa1和pa2,pa1实际指向基类对象a,pa2实际指向派生类对象b,将pa1和pa2分别作为参数传给func函数,func函数中尝试尝试使用dynamic_cast将它们强转类型转换为派生类指针,pa1转换失败,pa2转换成功。
class A
{
public:
virtual void func() { }
public:
int _a1 = 10;
};
class B : public A
{
public:
int _b1 = 20;
};
void func(A* pa)
{
//如果转换成功pb为正确的派生类对象地址
//如果转换失败pb为nullptr
B* pb = dynamic_cast<B*>(pa);
//如果转换成功
if (pb)
{
std::cout << "转换成功" << std::endl;
std::cout << "_a1 = " << pb->_a1 << std::endl;
std::cout << "_b1 = " << pb->_b1 << std::endl << std::endl;
}
else //如果转换失败
{
std::cout << "转换失败" << std::endl;
std::cout << "_a1 = " << pa->_a1 << std::endl << std::endl;
}
}
int main()
{
A a;
B b;
A* pa1 = &a;
A* pa2 = &b;
func(pa1); //转换失败
func(pa2); //转换成功
return 0;
}
三. 运行时类型识别 -- RTTI
C++通过以下三种方式,进行运行时类型失败:
- typeid -- 以字符串的形式获取变量/对象的类型
- dynamic_cast运算符 -- 检验基类的指针或引用能否转换为派生类的指针或引用。
- decltype关键字 -- 自动识别变量类型,可用其返回结果定义新的变量的类型。
四. 总结
- C语言支持隐式类型转换和强制类型转换,隐式类型转换发生在相近类型之间,强制类型转换用于不相关类型之间的转换。
- C++支持四种方式的类型转换,static_cast执行相近类型见的转换、reinterpret_cast用于不相关类型的强制转换,但不能取消const属性、const_cast用于取消对象类型的const属性、dynamic_cast用于将基类的指针或引用转换为派生类的指针或引用,dynamic_cast要求基类要有虚函数,否则会编译报错,dynamic_cast执行强制类型转换要么编译报错,如果转换成功就一定能保证安全。
- C++通过typeid、dynamic_cast和decltype三种方式实现运行时类型识别。