在C语言中,转换的可视性比较差,所有的转换形式都是以一种相同形式书写,难以跟踪错误的转换
转换使用场景:
(1)对对象(变量)进行初始化;
(2)对对象(变量)进行赋值;
(3)传参;
(4)返回值接收;
- static_cast
static_cast用于非多态类型的转换(静态转换),编译器隐式执行的任何类型转换都可用static_cast,但它不能用于两个不相关的类型进行转换(相当于C语言中隐式类型转换)
例如:
double d = 12.34;
int a = static_cast<int>(d);
- reinterpret_cast
从底层比特位层面进行转换,类似于C语言显式类型转换,例如:
double d = 1.34;
int* ptr = reinterpret_cast<int*>(&d);
- const_cast
const_cast最常用的用途就是删除变量的const属性,方便赋值
例如:
const int c = 2;
int* p = const_cast<int*>(&c);//删除const属性,方便赋值
- dynamic_cast
用于将一个父类对象的指针转换为子类对象的指针或引用(动态转换)
向上转型:子类对象指针->父类指针/引用(不需要转换,赋值兼容规则) 向下转型:父类对象指针->子类指针/引用(用dynamic_cast转换是安全的)
注意: 1. dynamic_cast只能用于含有虚函数的类 2. dynamic_cast会先检查是否能转换成功,能成功则转换,不能则返回0
例如:
class Base
{
public:
virtual void func()
{
cout << "Base::func()" << endl;
}
protected:
int _b;
};
class Derived :public Base
{
public:
void funcD()
{
cout << "Derived::funcD()" << endl;
}
protected:
int _d;
};
void fun(Base* pa)
{
pa->func();//实现多态
// dynamic_cast会先检查是否能转换成功,能成功则转换,不能则返回
Derived* pb1 = static_cast<Derived*>(pa);
Derived* pb2 = dynamic_cast<Derived*>(pa);
cout << "pb1:" << pb1 << endl;
cout << "pb2:" << pb2 << endl;
}
int main()
{
Base b;
Derived d;
//赋值兼容规则
b = d;
Base* pb = &d;
Base& rb = d;
Derived* pd = dynamic_cast<Derived*>(&b);//类的继承体系中必须有虚函数才能使用这种转换
fun(&b);
fun(&d);
}
最终的运行结果是:
将基类对象b传入函数fun中,使用static_cast可以成功转换,但是使用dynamic_cast不可以成功转换,第二次将子类对象传入,都可以成功转换;
此时pa是一个基类指针,不能调用子类中的成员函数,只能调用基类的成员函数,如果想要调用子类的成员函数,只能将pa转化为子类的指针,如果pa真正指向的是一个子类对象,代码是安全的,如果pa指向的是一个基类对象,代码不安全,dynamic_cast会进行检查,如果不安全返回空;
注意:强制类型转换关闭或挂起了正常的类型检查,每次使用强制类型转换前,程序员应该仔细考虑是否还有其他不同的方法达到同一目的,如果非强制类型转换不可,则应限制强制转换值的作用域,以减少发生错误的机会。强烈建议:避免使用强制类型转换;
- explicit
explicit关键字阻止经过转换构造函数进行的隐式转换的发生
例如:
class Date
{
public:
explicit Date(int year)
:_year(year)
, _month(1)
, _day(1)
{
cout << "Date::Date(int)" << endl;
}
Date& operator=(const Date& d)
{
if (this != &d)
{
_year = d._year;
_month = d._month;
_day = d._day;
return *this;
}
}
~Date()
{
cout << "Date::~Date()" << endl;
}
private:
int _year;
int _month;
int _day;
};
int main()
{
Date d1(2019);
//1、用单参构造函数,用2020构造一个临时对象
//2、用构造好的临时对象给d1赋值
//3、赋值结束后,临时对象将被销毁
//因此这种转换是正确的,如果不是单参构造函数,这里就是错误的,
//可以使用explicit关键字来阻止经过转换构造函数进行的隐式转换的发生
//d1 = 2020;//错误
return 0;
}
为什么C++需要四种类型转换?
C风格的转换格式很简单,但是有不少缺点的:
- 隐式类型转化有些情况下可能会出问题
- 显式类型转换将所有情况混合在一起,代码不够清晰
- RTTI
RTTI:Run-time Type identification的简称,即:运行时类型识别。
C++通过以下方式来支持RTTI:
(1)typeid运算符
(2)dynamic_cast运算符
源代码(github):
https://github.com/wangbiy/C-3/tree/master/test_2019_11_19/test_2019_11_19