C++在兼容C的强制类型转换后,提出了自己的类型转换模式
在C语言中,如果赋值运算符左右两侧类型不同,或者形参与实参类型不陪陪,或者返回值类型与接收返回值类型不一致时,就需要发生类型转换
C语言有两种类型的转换:隐式类型转换和显式类型转换
在赋值运算中,赋值号两边的数据类型不同时,需要把右边表达式的类型转换为左边变量的类型,这可能会导致数据失真,或者精度降低;所以说,隐式类型转换并不一定是安全的。对于不安全的类型转换,编译器一般会给出警告
无论是隐式类型转换还是显示类型转换,都只是为了本次运算而进行的临时性转换,转换的结果也会保存到临时的内存空间,不会改变数据本来的类型或者值
- void Test()
- {
- int i = 1;
- // 隐式类型转换
- double d = i;
- printf("%d, %.2f\n", i, d);
- int* p = &i;
- // 显示的强制类型转换
- int address = (int)p;
- printf("%x, %d\n", p, address);
- }
C语言风格的转换格式很简单,但存在如下缺点:①隐式类型转换导致数据精度丢失②显示类型转换混杂所有情况,代码不够清晰
因此C++提供了四种类型转换
用于非多态类型的转换,任何编译器执行的隐式类型转换都可以使用
static_cast必须用于相关类型的转换
static_cast是编译时静态类型检查,使用static_cast可以尽量发挥编译器的静态类型检查功能,但是并不能保证代码一定“正确”
- void test_1()
- {
- double d = 12.34;
- int a = static_cast<int>(d);
- cout << a << endl;
-
- }
用于为操作数的位模式提供较低层次的重新解释,用于将一种类型转换为另一种类型
一般多用于指针转换
- void test_2()
- {
- double d = 12.34;
- int a = static_cast<int>(d);
- cout << a << endl;
-
- // 这里使用static_cast会报错,应该使用reinterpret_cast
- //int *p = static_cast
(a); - int* p = reinterpret_cast<int*>(a);
- }
用于改变变量的const属性或volatile属性
Volatile意思是“易变的”,应该解释为“直接存取原始内存地址”比较合适。volatile 关键字是一种类型修饰符,用它声明的类型变量表示可以被某些编译器未知的因素更改。因此再增加volatile关键字,下面的代码中就可以进行修改
const_cast为什么能修改const为非const?
因为const变量不是物理上(将变量写在只读内存区)实现的,而是编译的时候由编译器保证不被修改的
- void test_3()
- {
- const int a = 2;
- int* p = const_cast<int*>(&a);
- //int* p = (int*)&a;//也可以实现
- *p = 3;
- cout << a << endl;//2
- //编译器实际上对a进行了修改,但是由于const限制,编译器对a进行优化,阻止a的改变
- cout << *p << endl;
-
- //volatile const int a = 2;
- //int* p = const_cast
(&a); - //*p = 3;
- //cout << a << endl;//3
- //cout << *p << endl;
- }
dynamic的意思是动态的。dynamic_cast也就是动态转换,将父类对象的指针/引用转换为子类对象的指针或引用
向上转型:子类对象的指针/引用->父类指针/引用(不需要使用,子类继承父类,兼容父类)
向下转型:父类对象的指针/引用->子类指针/引用(用dynamic_cast转型时安全的)
注意:①dynamic_cast只能用于父类含有虚函数的类
②dynamic_cast会先检查能否转换成功,如果能则转换,不能则返回0
- class A
- {
- public:
- virtual void f() {}
- };
- class B : public A
- {};
-
-
- void func(A* pa,const string& s)
- {
- cout << "pa指向" << s << endl;
- B* pb1 = (B*)pa;//不安全的
- B* pb2 = dynamic_cast(pa);//安全的
- cout << "强制转换\tpb1:" << pb1 << endl;
- cout << "dynamic_cast\tpb2:" << pb2 << endl;
- }
-
-
-
- void test_4()//dynamic_cast动态转换
- {
- A a;
- B b;
- func(&a, "指向父类对象");
- func(&b, "指向子类对象");
- }
RTTI(Run-Time Type Identification)-运行时类型识别,使程序能够获取由指针或引用指向对象的实际派生类型
C++为了支持RTTI,提供了dynamic_cast、typeid、decltype三种方法
typeid会将获取到的类型信息保存到一个type_info类型对象中,并返回该对象的const引用。如果需要具体的类型信息,可以通过成员函数来提取
为了减小编译后文件的体积,编译器不会为所有类型创建type_info对象,只会为使用了typeid运算符的类型创建。只有带虚函数的类,不论是否使用typeid运算符,编译器都会创建type_info对象
typeid是操作符,而不是函数
- #include
- #include
//需要添加的头文件 - using namespace std;
- void main()
- {
- //typeid().name()可以返回变量、函数、类的数据类型名,功能是相当强大的
- //注意:对非引用类型,typeid().name()是在编译时期识别的,只有引用类型才会在运行时识别
-
- const int a = 10;
- cout << typeid(&a).name() << endl;//const int *
-
- cout << typeid(typeid(a).name()).name() << endl;
-
- //结果为char const *,因此typeid().name()返回了存储类型名的字符串。
-
-
-
- }
注意:不可以使用typeid().name()作为变量类型
decltype是一种类型说明符,目的是解决复杂类型的什么。decltype并不计算表达式的值,而是通过编译器分析表达式并得到它的类型
①作用于变量直接得到变量的类型;②作用于表达式,结果是左值的表达式得到类型的引用,结果是右值的表达式得到类型;③作用于函数名会得到函数类型,不会自动转换成指针。
- const int ci = 0, &cj = ci;
-
- // x的类型是const int
- decltype(ci) x = 0;
-
- // y的类型是const int &
- decltype(cj) y = x;