类型转换机制可以分为:隐式类型转换 和 显示类型转换(强制类型转换)

C中的类型转换:

  事情要从头说起,这个头就是C语言.我们已经习惯了使用C-like类型转换,因为它强大而且简单.

主要有一下两种形式:

  • (new-type) expression
  • new-type (expression)

C艹中的类型转换:

隐式类型转换比较常见,在混合类型表达式中经常发生.比如在表达式中存在short和int,那么就过会发生整型提升.四种强制类型转换操作符:static_cast、dynamic_cast、const_cast、reinterpret_cast。

1. static_cast与dynamic_cast:

  把这两个放在一起比较容易记忆,"一静一动".从字面上也可以看出,前者提供的是编译时期的静态类型检测,后者提供的是

运行时检测.

  • static_cast: 1)完成基础数据类型,2)同一个继承体系中类型的转换 3)任意类型与空指针类型void*之间的转换。
  • dynamic_cast:使用多态的场景,增加了一层对真实调用对象类型的检查
  1. char c = ;
  2. int *p = (int *)&c;
  3. cout<<(char)*p<<endl;//'A'
  4. *p = ;
  5. int *q = static_cast<int *>(&c); //编译报错:error: invalid static_cast from type ‘char*’ to type ‘int*’

  在上面的例子中,Clike可以运行,然而修改过的*p如果使用,运行结果将会出现错误,而使用static_cast可以将错误在编译时期检查出,.

  在不同继承体系的自定义类型中:

  1. class A
  2. {
  3. public:
  4. A(){}
  5. ~A(){}
  6.  
  7. private:
  8. int i, j;
  9. };
  10.  
  11. class C
  12. {
  13. public:
  14. C(){}
  15. ~C(){}
  16.  
  17. void printC()
  18. {
  19. std::cout <<"call printC() in class C" <<std::endl;
  20. }
  21. private:
  22. char c1, c2;
  23. };
  24.  
  25. A *ptrA = new A();
  26. C *ptrC = (C *)(ptrA);
  27. ptrC->printC(); //"call printC() in class C"
  28. //ptrC = static_cast<C*>(ptrA); //编译报错:error: invalid static_cast from type 'A*’ to type C*’

  上面A C是两个无关的类,然而使用Clike可以实现这种类型的强制转换,这是十分危险的! 使用static_cast可以将这种潜在的危险在编译器找出来.

  在同一继承体系中:

  upcast(向上转换即子类转成父类):没有问题.因为父类的行为都包含在子类中;

  downcast(向下转换):有可能会出现问题,编译时可能不会发现.

  一个类的行为和自身的类型相关.也就是一个A类型的指针总会优先调用自己A类内的函数,当然发生继承中的重写(虚继承等)例外.

  1. #include <iostream>
  2. #include <cstdio>
  3.  
  4. using namespace std;
  5. class A
  6. {
  7. public:
  8. A():i(), j(){}
  9. ~A(){}
  10.  
  11. void printA()
  12. {
  13. std::cout <<"call printA() in class A" <<std::endl;
  14. }
  15.  
  16. void printSum()
  17. {
  18. std::cout <<"sum = " <<i+j <<std::endl;
  19. }
  20.  
  21. private:
  22. int i, j;
  23. };
  24.  
  25. class B : public A
  26. {
  27. public:
  28. B():a(), b() {}
  29. ~B(){}
  30.  
  31. void printB()
  32. {
  33. std::cout <<"call printB() in class B" <<std::endl;
  34. }
  35.  
  36. void printSum()
  37. {
  38. std::cout <<"sum = " <<a+b <<std::endl;
  39. }
  40.  
  41. void Add()
  42. {
  43. a++;
  44. b++;
  45. }
  46.  
  47. private:
  48. double a, b;
  49. };
  50. int main()
  51. {
  52. B *ptrB = new B;
  53. ptrB -> printSum();
  54. A *ptrA = static_cast<B *>(ptrB);
  55. ptrA -> printA();
  56. ptrA -> printSum();
  57. //打印结果:sum = 2
  58. //在进行upcast的时候,指针指向的对象的行为与指针的类型相关。
  59.  
  60. ptrA = new A;
  61. ptrB = static_cast<B *>(ptrA);
  62. ptrB -> printB();
  63. ptrB -> printSum();
  64. //打印结果:sum = 0
  65. //在进行downcast的时候,其行为是“undefined”。
  66.  
  67. B b;
  68. B &rB = b;
  69. rB.printSum();
  70. //打印结果: sum = 4
  71. A &rA = static_cast<A &>(b);
  72. rA.printA();
  73. rA.printSum();
  74. //打印结果: sum = 2
  75. //在进行upcast的时候,指针指向的对象的行为与引用类型相关.
  76.  
  77. A a;
  78. A &rA1 = a;
  79. rA.printSum();
  80. B &rB1 = static_cast<B &>(a);
  81. rB1.printB();
       //打印结果:sum = 4
  82. rB1.printSum();
  83. //打印结果 :sum = 1.45863e-316
  84. //在进行downcast的时候,其行为是“undefined”。
  85.  
  86. return ;
  87. }

  这里其实很明显,在downcast转换的时候,会出现一些跟指针或者引用类型相关的函数调用,但是因为指针或者引用(父类)

没有定义这些行为,因为调用到了这些行为导致出现了未定义的行为.

  明显解决这个问题的办法就是,虚函数!如果声明A类中的printSum未 虚函数,那么子类B就会有一个虚表,虚表中的第一个函数就是printSum函数其实是

B类的该函数.所以,A类指针调用该函数就会调用B类中的该函数 显示结果sum= 4. 在未定义之前sum = 2(A类中的该函数).

  PS:引用类型必须被初始化,这是引用和指针类型的重要区别.

  总之,就是尽可能不要使用downcast也就是 使用子类的指针指向父类.

  感觉这里又不得不说,c++内存对象的对齐方式.所以 ,在另外一篇blog<c++内存的对齐方式>中理清楚这些问题.

dynamic_cast
1.dynamic_cast是在运行时检查的,用于在集成体系中进行安全的向下转换downcast(当然也可以向上转换,但没必要,因为可以用虚函数实现)

即:基类指针/引用 -> 派生类指针/引用

如果源和目标没有继承/被继承关系,编译器会报错!
2.dynamic_cast是4个转换中唯一的RTTI操作符,提供运行时类型检查。
3.dynamic_cast不是强制转换,而是带有某种”咨询“性质的,如果不能转换,返回NULL。这是强制转换做不到的。

4.源类中必须要有虚函数,保证多态,才能使用dynamic_cast<source>(expression)

static_cast

用法:static_cast < type-id > ( expression )

该运算符把expression转换为type-id类型,在编译时使用类型信息执行转换,在转换执行必要的检测(指针越界,类型检查),其操作数相对是安全的。

但没有运行时类型检查来保证转换的安全性。

reinterpret_cast

仅仅是复制n1的比特位到d_r, 没有进行必要的分析.interpret_cast是为了映射到一个完全不同类型\
的意思,这个关键词在我们需要把类型映射回原有类型时用到它。我们映射到的类型仅仅是为了故弄\
玄虚和其他目的,这是所有映射中最危险的。(这句话是C++编程思想中的原话

const_cast

去除const常量属性,使其可以修改

and

volatile属性的转换  易变类型<->不同类型.

Code:

  1. #include <iostream>
  2. #include <cstdio>
  3. #include <string>
  4.  
  5. using namespace std;
  6.  
  7. class A {
  8. public:
  9. A(){};
  10. int m_a;
  11. };
  12.  
  13. class B {
  14. public:
  15. int m_b;
  16. };
  17.  
  18. class C : public A, public B {};
  19.  
  20. int main()
  21. {
  22. const A a;
  23. //a.num = 1;
  24. const_cast<A&>(a).m_a = ;
  25. //a.num = 3;编译不能通过,说明const_cast只能转换一次,不是永久脱离原有const属性
  26. cout<<a.m_a<<endl;
  27. int n = ;
  28. double d_s = static_cast<double>(n);
  29. double d_r = reinterpret_cast<double&>(n);
  30. cout<<d_r<<endl;//4.24399e-314
  31. //在进行计算以后, d_r包含无用值. 这是因为 reinterpret_cast\
  32. 仅仅是复制n1的比特位到d_r, 没有进行必要的分析.interpret_cast是为了映射到一个完全不同类型\
  33. 的意思,这个关键词在我们需要把类型映射回原有类型时用到它。我们映射到的类型仅仅是为了故弄\
  34. 玄虚和其他目的,这是所有映射中最危险的。(这句话是C++编程思想中的原话
  35.  
  36. C c;
  37. printf("%p, %p, %p\n", &c, reinterpret_cast<B*>(&c), static_cast <B*>(&c));
  38. //前两个的输出值是相同的,最后一个则会在原基础上偏移4个字节,这是因为static_cast计算了父子类指针转换的偏移量,\
  39. 并将之转换到正确的地址(c里面有m_a,m_b,转换为B*指针后指到m_b处),而reinterpret_cast却不会做这一层转换\
  40. 因此, 你需要谨慎使用 reinterpret_cast.
  41.  
  42. return ;
  43. }
  1. #include <iostream>
  2. #include <typeinfo>
  3. #include <cstdio>
  4.  
  5. using namespace std;
  6.  
  7. class A{
  8. public:
  9. virtual void foo(){
  10. cout<<"A foo"<<endl;
  11. }
  12. //虚函数的出现会带来动态机制 Class A 至少要有一个虚函数
  13. void pp(){
  14. cout<<"A pp"<<endl;
  15. }
  16. };
  17.  
  18. class B: public A{
  19. public:
  20. void foo(){
  21. cout<<"B foo"<<endl;
  22. }
  23. void pp(){
  24. cout<<"B PP"<<endl;
  25. }
  26. void functionB(){
  27. cout<<"Excute FunctionB!"<<endl;
  28. }
  29. };
  30.  
  31. int main()
  32. {
  33. B b;
  34. A *pa = &b;
  35. pa->foo();
  36. pa->pp();
  37. //基类指针可以指向派生类,但是只能调用基类和派生类都存在的成员,也就是说不能调用派生类中新增的成员!
  38. //pa->FunctionB();//error: 'class A' has no member named 'FunctionB'
  39. if(dynamic_cast<B*>(pa) == NULL){
  40. cout<<"NULL"<<endl;
  41. }else{
  42. cout<<typeid((dynamic_cast<B*>(pa))).name()<<endl;
  43. dynamic_cast<B*>(pa)->foo();
  44. dynamic_cast<B*>(pa)->pp();
  45. dynamic_cast<B*>(pa)->functionB();
  46. }
  47. A aa;
  48. //B *pb = &aa;派生类不能指向基类
  49. B *pbNull = NULL;
  50. pbNull->functionB();//fine
  51. pbNull->pp();//fine
  52. //pbNull->functionB(); crash!foo调用了虚函数,编译器需要根据对象的虚函数指针查找虚函数表,但为空,crash!
  53. return ;
  54. }

c++ 四种类型转换机制的更多相关文章

  1. C++语言中的四种类型转换

    1 引子 这篇笔记是根据StackOverflow上面的一个问题整理而成,主要内容是对C/C++当中四种类型转换操作进行举例说明.在之前其实对它们都是有所了解的,而随着自己在进行总结,并敲了一些测试示 ...

  2. 【C++】类型转换简述:四种类型转换方式的说明及应用

    本文主要简述在C++中四种类型转换的方式:static_cast.reniterpret_cast.const_cast和dynamic_cast. 在介绍C++类型转换方式之前,我们先来看看C语言的 ...

  3. C++ 四种类型转换

    在写代码中经常会有很多的隐式类型转换或显式类型转换. 对于隐式的类型转换主要是放生在赋值的时候,讲变量赋值给不同类型的变量的时候就会发生类型转换,如果是宽化转换(即从占字节少的类型向占字节多的类型转换 ...

  4. [转]C++中四种类型转换符的总结

    C++中四种类型转换符的总结 一.reinterpret_cast用法:reinpreter_cast<type-id> (expression)    reinterpret_cast操 ...

  5. 从零开始学C++之从C到C++(二):引用、内联函数inline、四种类型转换运算符

    一.引用 (1).引用是给一个变量起别名 定义引用的一般格式:类型  &引用名 = 变量名: 例如:int a=1; int  &b=a;// b是a的别名,因此a和b是同一个单元 注 ...

  6. c++ --> c++中四种类型转换方式

    c++中四种类型转换方式   c风格转换的格式很简单(TYPE)EXPRESSION,但是c风格的类型转换有不少缺点, 1)它可以在任意类型之间转换,比如你可以把一个指向const对象的指针转换成指向 ...

  7. 【转】C++四种类型转换方式

    C++四种类型转换方式 https://blog.csdn.net/lv_amelia/article/details/79483579 C风格的强制类型转换(Type Case)很简单,不管什么类型 ...

  8. 引用、数组引用与指针引用、内联函数inline、四种类型转换运算符

    一.引用 (1).引用是给一个变量起别名 定义引用的一般格式:类型  &引用名 = 变量名: 例如:int a=1;  int  &b=a;// b是a的别名,因此a和b是同一个单元 ...

  9. C++中四种类型转换以及const_cast是否能改变常量的问题

    we have four specific casting operators:dynamic_cast, reinterpret_cast, static_cast and const_cast. ...

随机推荐

  1. PAT 1079. 延迟的回文数

    PAT 1079. 延迟的回文数 给定一个 k+1 位的正整数 N,写成 ak...a1a0 的形式,其中对所有 i 有 0 <= ai < 10 且 ak > 0.N 被称为一个回 ...

  2. BZOJ 1565 Luogu P2805 [NOI2009]植物大战僵尸 (Tarjan判环、最小割)

    我: "立个flag 14点之前调完这题" 洛谷AC时间: 2019-06-24 14:00:16 实力打脸... 网络流板子从来写不对系列 题目链接: (BZOJ) https: ...

  3. Shell中的循环语句实例

    1.for循环语句实例1.1 最基本的for循环 #!/bin/bash for x in one two three four do     echo number $x done 注:" ...

  4. GUI 总结(一)

    一/概述 1.两个包: javax.awt //before java 1.2 javax.swing //after java 1.2 2.两个词: 组件Component 容器Container ...

  5. java复习volatile关键字解析

    转自https://www.cnblogs.com/dolphin0520/p/3920373.html volatile这个关键字可能很多朋友都听说过,或许也都用过.在Java 5之前,它是一个备受 ...

  6. 武大OJ 622. Symmetrical

    Description          Cyy likes something symmetrical, and Han Move likes something circular. Han Mov ...

  7. html缓存机制,http头部控制

    1.缓存分类:服务器缓存(协商缓存),第三方缓存,浏览器缓存(强制缓存) 2.浏览器缓存(添加 meta),设置请求指定的http头部信息.(状态码200,from cache , from dist ...

  8. stack、queue实现

    //SGI STL以deque作为缺省情况下的stack底部结构,stack没有迭代器,不提供遍历功能 //queue的实现类似stack,也是以deque作为缺省底层结构 template < ...

  9. 云上kafka和自建kafka对比

    说起Kafka,许多使用者对它是又爱又恨.Kafka是一种分布式的.基于发布/订阅的消息系统,其极致体验让人欲罢不能,但操心的运维.复杂的安全策略.可靠性易用性的缺失.算不上极致的性能发挥.并不丰富的 ...

  10. mac 显示隐藏文件的命令行和快捷键

    命令行方式: 显示隐藏文件: defaults write com.apple.Finder AppleShowAllFiles YES;KillAll Finder 不显示隐藏文件: default ...