目录
1、c语言中的类型转换
c++中的类型转换
1、内置类型和自定义类型的隐式转换(借助构造)
2、自定义类型和自定义类型的隐式转换(借助构造)
3、内置类型转自定义类型的隐式转换(重载一个operator 类型)
新增的四种强制类型转换
1、static_cast
2、reinterpret_cast
3、const_cast
4、dynamic_cast
总结:
4、RTTI
重点:
1、c语言中的类型转换
在c语言中,如果赋值运算符左右两侧类型不同,或者形参与实参类型不匹配,或者返回值类型与接受返回值类型不一致时,就需要发生类型转化。
c语言中有2种类型转换:隐式类型转换、显式类型转换
隐式:编译器在编译阶段自动进行,能赚就转,不能转就编译失败
显示:需要用户自己处理。
1、有一定关联的才可以互相转换
跟整型搭边的,才可以进行隐式类型转换,如下
char-整型,浮点数-整型 ,有符合-无符号,这些转换会涉及截断、提升
截断和提升可以看我另外的文章《关于操作符的内容》
bool和整形 bool和指针(指针为空指针就是0,0就是假,非空指针就是非0,非0就是真)
2、指针和整型,有一定关联,指针本身也是数字编号
所以虽然不能隐式类型转换,但强制的、显式的转换是可行的3、不同类型之间可以借用指针进行强制转换
比如h1是个自定义对象,我们如果只想要这个对象的前4个字节
arc *p1=&h1;
int* x=(int*)p1;
这样就可以把指针强制转换类型,然后就可以访问前4个字节(int占4字节)。int main() {int a1 = 1;//隐式double a2 = a1;printf("%d,%.2f\n", a1, a2);int* p = &a1;//显式int ad = (int)p;printf("%x,%d\n", p, ad);return 0; }
缺陷:隐式类型转换,可能出现精度丢失的问题(如double-int)
显式转换把所有情况都混合在一起,代码不清晰
比如无符号无法小于0,导致无符号与无符号比较时,可能出现死循环等问题
又比如int跟size_t比较,int会隐式转换成size_t,而负数的int也会照样转,所以还是会出问题。
size_t x=0; size_t a=10; while(a>=x) {cout<<a<<endl;x--; } //会死循环size_t x=0; int a=10; while(a>=x) {cout<<a<<endl;x--; }//会死循环
c++中的类型转换
c++是兼容c的,所以c的也能用,c++只是较于c新增了一些内容。
1、内置类型和自定义类型的隐式转换(借助构造)
string s = "231213"; //单参数的构造函数,支持内置类型隐式转换成自定义类型。
上面就是将const char*通过隐式类型转换,在编译器不优化的情况下:先用const char*构造一个临时的string对象,再调用s的拷贝构造,把这个临时对象拷贝给s。优化之后就是直接用const char*构造s。
2、自定义类型和自定义类型的隐式转换(借助构造)
比如vector增加的initializer_list参数的构造函数,stack<pair<int,int>>,都是把一个自定义对象作为构造的参数之一,来构造一个新的自定义对象。
3、内置类型转自定义类型的隐式转换(重载一个operator 类型)
class pl { public://特殊写法,不要加返回值,否则会跟仿函数冲突operator int() {return x1 * x2;} private:int x1 = 1, x2 = 2; };int main() {pl x3;//int a = (int)x3;也可以int a = x3;cout << a << endl;return 0; }
新增的四种强制类型转换
1、static_cast
用于非多态类型的转换(静态转换),编译器隐式执行的任何类型转换都可用static_cast,但不能用于两个不相关的类型进行转换。
int main() {float x = 10.10;int f = static_cast<int>(x);cout << f << endl;//10return 0; }
2、reinterpret_cast
通常为操作数的位模式提供较低层次的重新解释,比如指针。对应c语言的强制类型转换
int main() {int a = 10;//int* p = static_cast<int*>(a);会报错,因为指针必须用强转int* p = reinterpret_cast<int*>(&a);return 0; }
3、const_cast
const_cast最常用的用途就是删除变量的const属性,方便赋值,但本身也是强制类型转换,只是单独的把去const的功能拎出来,并且提醒const有一定编译器优化的风险,具体看下面。
int main() {const int a = 10;int* p = const_cast<int*>(&a);*p = 10;return 0; }
这个要注意一个坑
int main() {const int a = 10;int* p = const_cast<int*>(&a);*p = 5;cout << a << endl;//10cout << (*p) << endl;//5return 0; }
因为编译器的优化,导致对于这些const修饰的变量会复制一份到寄存器,而p接受的地址,是a变量的内存的地址,虽然修改成了5,也只是改了在内存的那一份,而寄存器的仍旧是10
如果用监视窗口查看,会发现在监视窗口a已经被改成了5,因为监视窗口针对的是内存。
为此,我们可以a加个修饰 volatile const int a 加了这个修饰,就不会复制一份到寄存器
4、dynamic_cast
用于将一个父类对象的指针/引用转换为子类对象的指针或引用(动态转换)
向上转型:子类对象指针/引用 -》父类指针/引用(不需要转换,直接切片拷贝过去,是遵循继承的赋值兼容规则的)
向下转型:父类对象指针/引用-》子类指针/引用(用dynamic_cast转型是安全的)
因为子类对象可能会有新的成员,而如果利用赋值兼容,把父类对象给了子类指针,那么子类指针访问子类的新的成员,会有越界的风险问题。
注意事项:
1、dynamic_cast只用于父类含有虚函数的类。
2、dynamic_cast会先检查是否能转化成功,能成功则转换,不成功返回0。
class F { public:virtual void f() {} }; class X :public F {}; void fun(F* pa) {//有风险//X* pb = (X*)pa;X* pb = dynamic_cast<X*>(pa);if (pb){cout << "成功" << endl;}else{cout << "失败" << endl;} } int main() {F a;X b;fun(&a);//失败fun(&b);//成功return 0; }
总结:
强制类型转换关闭或者说是挂起了正常的类型检查,每次使用强转前,先想想能不能不转也达到目的,如果非转不可,就限制转换出来的值的作用域,减少发生错误的概率。
4、RTTI
RTTI:Run-time Type identification 的简称,即:运行时类型识别
c++通过以下方式支持RTTI:
typeid、dynamic_cast、decltype
重点:
c++新增的4种强制类型转换是什么
这几个转换的应用场景是什么