目录
C语言类型转换
//c语言类型转换
void testCast()
{
//隐式类型转换
//内置类型
int i = 10;
char c = i;
//不支持转换,类型差别过大,但可以强转
int *pi = &i;
int i2 = pi;
//强制转换,但像下面的这样没有意义
int *pi = &i;
int i2 = (int)pi;
}
缺陷: 转换的可视性比较差,所有的转换形式都是以一种相同形式书写,难以跟踪错误的转换
为什么C++需要四种类型转换
因为从上面可以看出C风格的转换格式很简单,但是有不少缺点的:
- 隐式类型转化有些情况下可能会出问题
- 显式类型转换将所有情况混合在一起,代码不够清晰
C++类型转换
标准C++为了加强类型转换的可视性,引入了四种命名的强制类型转换操作符:static_cast、reinterpret_cast、const_cast、dynamic_cast。
static_cast
static_cast用于非多态类型的转换(静态转换),编译器隐式执行的任何类型转换都可用static_cast,但它不能用于两个不相关的类型进行转换。
void testCast2()
{
int i = 10;
//静态转换:static_cast
//支持任何隐式类型转换 但它不支持两个不相关的类型进行强制转换
char c = static_cast<char>(i);
int i3 = static_cast<int> (pi);//报错 不支持两个不相关的类型进行强制转换
}
reinterpret_cast
reinterpret_cast操作符通常为操作数的位模式提供较低层次的重新解释,用于将一种类型转换为另一种不同的类型 。
void testCast3()
{int i = 10;
//强制类型转换:reinterpret_cast
//重新解释,用于将一种类型转换为另一种不同的类型
int* pi = &i;
int i2 = reinterpret_cast<int> (pi);}
const_cast
常用于删除变量的const属性,方便赋值
void testCast3()
{
//const_cast: 去除变量的const属性
const int ci = 20;
const int* pci = &ci;
//想要用 int*指针接收const int ci地址,可以使用 const_cast
int* pci2 = const_cast<int*> (&ci);
*pci2 = 30;
cout << ci << endl; //20
cout << *pci2 << endl;//30
cout << *pci << endl; //30
cout << "adress" << endl; //地址都一样 编译器做了优化
cout << &ci << endl;
cout << pci2 << endl;
cout << pci << endl;
}
dynamic_cast
dynamic_cast 用于将一个父类对象的指针转换为子类对象的指针或引用(动态转换)
- 向上转型:子类对象指针->父类指针/引用(不需要转换,赋值兼容规则)
- 向下转型:父类对象指针->子类指针/引用(用dynamic_cast转型是安全的)
注意: 1. dynamic_cast只能用于含有虚函数的类 2. dynamic_cast会先检查是否能转换成功,能成功则转换,不能则返回0。
class A
{
public:
virtual void fun()
{
cout << "A: fun()" << endl;
}
};
class B:public A
{
public:
virtual void fun()
{
cout << "B: fun()" << endl;
}
};
void testCast4()
{
B b;
//切片 子类---》父类
A a = b;
A& rb = b;
A* pb = &b;
A aobj;
B* pa = (B*)&aobj;//父类-》子类 需要强转
B* pa2 = reinterpret_cast<B*> (&aobj);//可以强转但是不安全,容易访问越界。
B* pa3 = dynamic_cast<B*> (&aobj);
//static_cast没有类型安全检查,解引用可能会有问题。
B* pa4 = static_cast<B*> (&aobj);//可以转换,没有语法错误,但是没有类型安全检查
cout << pa << endl; //0032FA30 pa和pa2地址一样
cout << pa2 << endl;//0032FA30
cout << pa3 << endl;//不安全 00000000
B* pb1 = dynamic_cast<B*> (pb);//pb: 指向子类对象的父类指针 因此可以转换是安全的
}
dynamic_cast 测试非多态
//非多态不可用
class C
{};
class D :public C
{};
void testCast5()
{
//非多态不可使用 D 和 C没构成多态场景
C c;
D* pc = dynamic_cast<D*> (&c);
}
explicit
explicit关键字阻止经过转换构造函数进行的隐式转换的发生
//单参构造的隐式类型转换举例
class E
{
public:
E(int a)
:_a(a)
{
cout << "E(int a) " << endl;
}
private:
int _a;
};
int main()
{
//单参构造的隐式类型转换
E e = 10;//把一个整数类型赋给自定义类型,会支持
//实际上是它会用10调用构造函数创建一个匿名的E类型的对象,再把这个匿名对象赋给e
return 0;
}
实际上面的代码在执行时:它会用10调用构造函数创建一个匿名的E类型的对象,再把这个匿名对象赋给e。类型一致,赋值当然没错。
//多参构造的隐式类型转换举例
class E
{
public:
E(int a, int b)
:_a(a)
{}
private:
int _a;
};
int main()
{
E e2 = 10, 20;
return 0;
}
//explicit
class E
{
public:
//explicit会阻止单参构造的隐式类型转换
explicit E(int a)
:_a(a)
{
cout << "E(int a) " << endl;
}
private:
int _a;
};
int main()
{
//explicit关键字会阻止单参构造函数进行的隐式转换
E e = 10;
return 0;
}
C++中的RTTI机制
RTTI:Run-time Type identification的简称,即:运行时类型识别。
RTTI提供了以下两个非常有用的操作符:
- typeid操作符,返回指针和引用所指的实际类型。
- dynamic_cast操作符,将基类类型的指针或引用安全地转换为派生类型的指针或引用。
小结
因此使用强制转换方法的选择 如下
- 去const属性用const_cast
- 基本类型转换用static_cast
- 多态类之间的类型转换用dynamic_cast
- 不同类型的指针类型转换用reinterpret_cast
强制类型转换实际上 关闭或挂起了正常的类型检查,每次使用强制类型转换前,程序员应该仔细考虑是否还有其他不同的方法达到同一目的,如果非强制类型转换不可,则应限制强制转换值的作用域,以减少发生错误的机会。
强烈建议:避免使用强制类型转换