




已阅读5页,还剩8页未读, 继续免费阅读
版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
标准C+的类型转换符:static_cast、dynamic_cast、reinterpret_cast和const_cast1 C 风格(C-style)强制转型如下所示:(T) exdivssion / cast exdivssion to be of type T函数风格(Function-style)强制转型使用这样的语法:T(exdivssion) / cast exdivssion to be of type T这两种形式之间没有本质上的不同,它纯粹就是一个把括号放在哪的问题。我把这两种形式称为旧风格(old-style)的强制转型。2 标准C+的类型转换符使用标准C+的类型转换符:static_cast、dynamic_cast、reinterpret_cast、和const_cast。2.1 static_cast用法:static_cast ( exdivssion )该运算符把exdivssion转换为type-id类型,但没有运行时类型检查来保证转换的安全性。它主要有如下几种用法:1) 用于类层次结构中基类和子类之间指针或引用的转换。进行上行转换(把子类的指针或引用转换成基类表示)是安全的;进行下行转换(把基类指针或引用转换成子类表示)时,由于没有动态类型检查,所以是不安全的。2) 用于基本数据类型之间的转换,如把int转换成char,把int转换成enum。这种转换的安全性也要开发人员来保证。3)把空指针转换成目标类型的空指针。4)把任何类型的表达式转换成void类型。注意:static_cast不能转换掉exdivssion的const、volitale、或者_unaligned属性。2.2dynamic_cast用法:dynamic_cast ( exdivssion )该运算符把exdivssion转换成type-id类型的对象。Type-id必须是类的指针、类的引用或者void *;如果type-id是类指针类型,那么exdivssion也必须是一个指针,如果type-id是一个引用,那么exdivssion也必须是一个引用。dynamic_cast主要用于类层次间的上行转换和下行转换,还可以用于类之间的交叉转换。在类层次间进行上行转换时,dynamic_cast和static_cast的效果是一样的;在进行下行转换时,dynamic_cast具有类型检查的功能,比static_cast更安全。class Bpublic:int m_iNum;virtual void foo();class D:public Bpublic:char *m_szName100;void func(B *pb)D *pd1 = static_cast(pb);D *pd2 = dynamic_cast(pb);在上面的代码段中,如果pb指向一个D类型的对象,pd1和pd2是一样的,并且对这两个指针执行D类型的任何操作都是安全的;但是,如果pb指向的是一个B类型的对象,那么pd1将是一个指向该对象的指针,对它进行D类型的操作将是不安全的(如访问m_szName),而pd2将是一个空指针。另外要注意:B要有虚函数,否则会编译出错;static_cast则没有这个限制。这是由于运行时类型检查需要运行时类型信息,而这个信息存储在类的虚函数表(关于虚函数表的概念,详细可见)中,只有定义了虚函数的类才有虚函数表,没有定义虚函数的类是没有虚函数表的。另外,dynamic_cast还支持交叉转换(cross cast)。如下代码所示。class Apublic:int m_iNum;virtual void f();class B:public A;class D:public A;void foo()B *pb = new B;pb-m_iNum = 100;D *pd1 = static_cast(pb); /compile errorD *pd2 = dynamic_cast(pb); /pd2 is NULLdelete pb;在函数foo中,使用static_cast进行转换是不被允许的,将在编译时出错;而使用 dynamic_cast的转换则是允许的,结果是空指针。2.3 reinterpret _cast用法:reinterpret_cast ( expression )type-id必须是一个指针、引用、算术类型、函数指针或者成员指针。它可以把一个指针转换成一个整数,也可以把一个整数转换成一个指针(先把一个指针转换成一个整数,在把该整数转换成原类型的指针,还可以得到原先的指针值)。该运算符的用法比较多。2.4 const_cast用法:const_cast ( expression )该运算符用来修改类型的const或volatile属性。除了const 或volatile修饰之外, type_id和exdivssion的类型是一样的。常量指针被转化成非常量指针,并且仍然指向原来的对象;常量引用被转换成非常量引用,并且仍然指向原来的对象;常量对象被转换成非常量对象。Voiatile和const类试。举如下一例:class Bpublic:int m_iNum;void foo()const B b1;b1.m_iNum = 100; /comile errorB b2 = const_cast(b1);b2. m_iNum = 200; /fine上面的代码编译时会报错,因为b1是一个常量对象,不能对它进行改变;使用const_cast把它转换成一个常量对象,就可以对它的数据成员任意改变。注意:b1和b2是两个不同的对象。3 不同转换之间的比较3.1 dynamic_cast .vs. static_castclass B . ;class D : public B . ;void f(B* pb)D* pd1 = dynamic_cast(pb);D* pd2 = static_cast(pb);If pb really points to an object of type D, then pd1 and pd2 will get the same value. They will also get the same value if pb = 0.If pb points to an object of type B and not to the complete D class, then dynamic_cast will know enough to return zero. However, static_cast relies on the programmers assertion that pb points to an object of type D and simply returns a pointer to that supposed D object.即dynamic_cast可用于继承体系中的向下转型,即将基类指针转换为派生类指针,比static_cast更严格更安全。dynamic_cast在执行效率上比static_cast要差一些,但static_cast在更宽上范围内可以完成映射,这种不加限制的映射伴随着不安全性。static_cast覆盖的变换类型除类层次的静态导航以外,还包括无映射变换、窄化变换(这种变换会导致对象切片,丢失信息)、用VOID*的强制变换、隐式类型变换等.3.2 static_cast .vs. reinterpret_castreinterpret_cast是为了映射到一个完全不同类型的意思,这个关键词在我们需要把类型映射回原有类型时用到它。我们映射到的类型仅仅是为了故弄玄虚和其他目的,这是所有映射中最危险的。(这句话是C+编程思想中的原话)static_cast 和 reinterpret_cast 操作符修改了操作数类型。它们不是互逆的; static_cast 在编译时使用类型信息执行转换,在转换执行必要的检测(诸如指针越界计算, 类型检查). 其操作数相对是安全的。另一方面;reinterpret _cast 仅仅是重新解释了给出的对象的比特模型而没有进行二进制转换, 例子如下:int n=9; double d=static_cast (n);上面的例子中, 我们将一个变量从 int 转换到 double。 这些类型的二进制表达式是不同的。 要将整数 9 转换到 双精度整数 9,static_cast 需要正确地为双精度整数 d 补足比特位。其结果为 9.0。而reinterdivt_cast 的行为却不同:int n=9;double d=reinterpret_cast (n);这次, 结果有所不同. 在进行计算以后, d 包含无用值. 这是因为 reinterpret_cast 仅仅是复制 n 的比特位到 d, 没有进行必要的分析.因此, 你需要谨慎使用 reinterpret_cast.4 用dynamic_cast的注意点当VC中用dynamic_cast时如果出现以下警告:warning C4541: dynamic_cast used on polymorphic type class CWnd with /GR-; unpredictable behavior may result是因为没有打开run-timetype information 支持就使用dynamic_cast 1 vector 向量 相当于一个数组vector 是序列容器,类似于数组的存储结构,线性存储,内存分配时占用连续空间,只是大小可以根据需要扩大,因为采用的是随机迭代器,所以得到某一位置的值非常快。不过开销比较大,插入和删除比较慢,因为涉及到大块内存的赋值粘贴。通常情况下如果涉及到比较少的插入,删除操作时候用它比较好。在内存中分配一块连续的内存空间进行存储。支持不指定vector大小的存储。STL内部实现时,首先分配一个非常大的内存空间预备进行存储,即capacituy()函数返回的大小, 当超过此分配的空间时再整体重新放分配一块内存存储,这给人以vector可以不指定vector即一个连续内存的大小的感觉。通常此默认的内存分配能完成大部分情况下的存储。 优点:(1) 不指定一块内存大小的数组的连续存储,即可以像数组一样操作,但可以对此数组进行动态操作。通常体现在push_back() pop_back()(2) 随机访问方便,即支持 操作符和vector.at()(3) 节省空间。缺点:(1) 在内部进行插入删除操作效率低。(2) 只能在vector的最后进行push和pop,不能在vector的头进行 push和pop。 (3) 当动态添加的数据超过vector默认分配的大小时要进行整体的重新分配、拷贝与释放 2 list 双向链表list也是容器,类似双向链表,非线性存储结构,内存分配是零散的,采用的是双向迭代器,比起vetor它做到了不浪费存储空间,对插入删除处理很快,但是得到某一位置的值并不快,一般涉及到经常性的插入删除操作时候,可以考虑它。 每一个结点都包括一个信息快Info、一个前驱指针Pre、一个后驱指针Post。可以不分配必须的内存大小方便的进行添加和删除操作。使用的是非连续的内存空间进行存储。 优点: (1) 不使用连续内存完成动态操作。(2) 在内部方便的进行插入和删除操作(3) 可在两端进行push、pop 缺点:(1) 不能进行内部的随机访问,即不支持 操作符和vector.at() (2) 相对于verctor占用内存多 3 deque 双端队列 double-end queue deque是在功能上合并了vector和list。 优点: (1) 随机访问方便,即支持 操作符和vector.at() (2) 在内部方便的进行插入和删除操作(3) 可在两端进行push、pop 缺点: (1) 占用内存多 4Map类似表一个key,一个value,是树状的存储存储,key不可重复。底层采用的是树型结构,多数使用平衡二叉树实现,查找某一值是常数时间,遍历起来效果也不错,只是每次插入值的时候,会重新构成底层的平衡二叉树,效率有一定影响。map提供了比较好的查找功能,本身它的存储结构也是为查找方便而设计。使用区别: 1 如果你需要高效的随即存取,而不在乎插入和删除的效率,使用vector 2 如果你需要大量的插入和删除,而不关心随即存取,则应使用list 3 如果你需要随即存取,而且关心两端数据的插入和删除,则应使用deque 3.5 标准库bitset类型有些程序要处理二进制位的有序集,每个位可能包含的是0(关)或1(开)的值。位是用来保存一组项或条件的yes/no信息(有时也称标志)的简洁 方法。标准库提供了bitset类使得处理位集合更容易一些。要使用bitset类就必须要包含相关的头文件。在本书提供的例子中,假设都使用了 std:bitset的using声明:#include using std:bitset;3.5.1 bitset的定义和初始化表3-6列出了bitset的构造函数。类似于vector,bitset类是一种类模板;而与vector不一样的是bitset类型对象的区别仅在其长度而不在其类型。在定义bitset时,要明确bitset含有多少位,须在尖括号内给出它的长度值:bitset bitvec; /32位,全为0。给出的长度值必须是常量表达式(2.7节)。正如这里给出的,长度值必须定义为整型字面值常量或是已用常量值初始化的整数类型的const对象。这条语句把bitvec定义为含有32个位的bitset对象。和vector的元素一样,bitset中的位是没有命名的,程序员只能按位置来访 问它们。位集合的位置编号从0开始,因此,bitvec的位序是从0到31。以0位开始的位串是低阶位(low-order bit),以31位结束的位串是高阶位(high-order bit)。表3-6 初始化bitset对象的方法bitset b;b有n位,每位都为0bitset b(u);b是unsigned long型u的一个副本bitset b(s);b是string对象s中含有的位串的副本bitset b(s, pos, n);b是s中从位置pos开始的n个位的副本1. 用unsigned值初始化bitset对象当用unsigned long值作为bitset对象的初始值时,该值将转化为二进制的位模式。而bitset对象中的位集作为这种位模式的副本。如果bitset类型长度大 于unsigned long值的二进制位数,则其余的高阶位置为0;如果bitet类型长度小于unsigned long值的二进制位数,则只使用unsigned值中的低阶位,超过bitet类型长度的高阶位将被丢弃。在32位unsigned long的机器上,十六进制值0xffff表示为二进制位就是十六个1和十六个0(每个0xf可表示为1111)。可以用0xffff初始化bitset对象:/ bitvec1 is smaller than the initializerbitset bitvec1(0xffff); / bits 0 . 15 are set to 1/ bitvec2 same size as initializerbitset bitvec2(0xffff); / bits 0 . 15 are set to 1; 16 . 31 are 0/ on a 32-bit machine, bits 0 to 31 initialized from 0xffffbitset bitvec3(0xffff); / bits 32 through 127 initialized to zero上面的三个例子中,0到15位都置为1。由于bitvec1位数少于unsigned long的位数,因此bitvec1的初始值的高阶位被丢弃。bitvec2和unsigned long长度相同,因此所有位正好放置了初始值。bitvec3长度大于32,31位以上的高阶位就被置为0。2. 用string对象初始化bitset对象当用string对象初始化bitset对象时,string对象直接表示为位模式。从string对象读入位集的顺序是从右向左:string strval(1100);bitset bitvec4(strval);bitvec4的位模式中第2和3的位置为1,其余位置都为0。如果string对象的字符个数小于bitset类型的长度,则高阶位将置为0。string对象和bitset对象之间是反向转化的:string对象的最右边字符(即下标最大的那个字符)用来初始化bitset对象的低阶位(即下标为0的位)。当用string对象初始化bitset对象时,记住这一差别很重要。不一定要把整个string对象都作为bitset对象的初始值。相反,可以只用某个子串作为初始值:string str(1111111000000011001101);bitset bitvec5(str, 5, 4); / 4 bits starting at str5, 1100bitset bitvec6(str, str.size() - 4); / use last 4 characters这里用str中从str5开始包含四个字符的子串来初始化bitvec5。照常,初始化bitset对象时总是从子串最右边结尾字符开始 的,bitvec5的从0到3的二进制位置为1100,其他二进制位都置为0。如果省略第三个参数则意味着取从开始位置一直到string末尾的所有字 符。本例中,取出str末尾的四位来对bitvec6的低四位进行初始化。bitvec6其余的位初始化为0。这些初始化过程的图示如下:3.5.2 bitset对象上的操作多种bitset操作(表3-7)用来测试或设置bitset对象中的单个或多个二进制位:表3-7 bitset操作1. 测试整个bitset对象如果bitset对象中有一个或多个二进制位置为1,则any操作返回true,也就是说,其返回值等于1;相反,如果bitset对象中的二进制位全为0,则none操作返回true。bitset bitvec; / 32 bits, all zerobool is_set = bitvec.any(); / false, all bits are zerobool is_not_set = bitvec.none(); / true, all bits are zero如果需要知道置为1的二进制位的个数,可以使用count操作,该操作返回置为1的二进制位的个数:size_t bits_set = bitvec.count(); / returns number of bits that are oncount操作的返回类型是标准库中命名为size_t的类型。size_t类型定义在cstddef头文件中,该文件是C标准库的头文件stddef.h的C+版本。它是一个与机器相关的unsigned类型,大小可以保证存储内存中对象。与vector和string中的size操作一样,bitset的size操作返回bitset对象中二进制位的个数,返回值的类型是size_t:size_t sz = bitvec.size(); / returns 322. 访问bitset对象中的位可以用下标操作符来读或写某个索引位置的二进制位,同样地,也可以用下标操作符测试给定二进制位的值或设置某个二进制位的值:/ assign 1 to even numbered bitsfor (int index = 0; index != 32; index += 2) bitvecindex = 1;上面的循环把bitvec中的偶数下标的位都置为1。除了用下标操作符,还可以用set、test和reset操作来测试或设置给定二进制位的值:/ equivalent loop using set operationfor (int index = 0; index != 32; index += 2) bitvec.set(index);为了测试某个二进制位是否为1,可以用test操作或者测试下标操作符的返回值:if (bitvec.test(i) / bitveci is on/ equivalent test using subscriptif (bitveci) / bitveci is on如果下标操作符测试的二进制位为1,则返回的测试值的结果为true,否则返回false。3. 对整个bitset对象进行设置set和reset操作分别用来对整个bitset对象的所有二进制位全置1和全置0:bitvec.reset(); / set all the bits to 0.bitvec.set(); / set a
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 建筑装修工程预算报价技巧解析
- 九年级数学期中期末试卷及解析
- 初中语文写作技巧与范文集锦
- 曝气沉砂池设计计算详解
- 市政道路砼挡墙质量控制措施
- 消防演习方案及应急处置预案
- 排污管道冲洗及化粪池清理验收流程
- 六年级英语毕业复习课程计划表
- 2016年秋季高三数学文科试题分析
- 护理部人员奖惩制度及执行标准
- 广东省深圳市2024-2025学年高一上学期期中考试数学试卷(含答案)
- 第6讲立体几何(2022-2023年高考真题)(原卷版)
- 安宁疗护舒适照护
- 幕墙作业安全技术交底
- 中医耳针技术
- 混凝土板底疏松修补方案
- 小型文化传媒公司财务管理制度
- 山东省第二届化学分析检验人员行业职业技能竞赛理论试题库资料(含答案)
- 人教版数学一年级(上册)知识点全
- 孕产妇健康管理服务规范
- AQ 1097-2014 井工煤矿安全设施设计编制导则(正式版)
评论
0/150
提交评论