1 / 38

多继承 (multiple inheritance) 指在一个继承树层次的剖 面上派生类可以拥有不止一个直接基类。多重继承是 C++ 语 言后期引进的代码重用的模式,单一继承对应派生类上逆时

第 22 章 多重继承和类型变换. 多继承 (multiple inheritance) 指在一个继承树层次的剖 面上派生类可以拥有不止一个直接基类。多重继承是 C++ 语 言后期引进的代码重用的模式,单一继承对应派生类上逆时 恒只有一个直接基类。多重继承打破了单一继承这种单枝独 上的简单性。 C 风格的类型转换对于系统级的操作是必不可少的。 C++ 鉴于 C 风格的类型转换未提供足够明显的安全保障 机制,特引进新形式的类型变换规则。. 五、新的类型变换. 五、新的类型变换 英文 cast 可翻译成变换、映射或投影。 ~_cast 类型变

nedra
Download Presentation

多继承 (multiple inheritance) 指在一个继承树层次的剖 面上派生类可以拥有不止一个直接基类。多重继承是 C++ 语 言后期引进的代码重用的模式,单一继承对应派生类上逆时

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. 第22章 多重继承和类型变换 • 多继承(multiple inheritance)指在一个继承树层次的剖 • 面上派生类可以拥有不止一个直接基类。多重继承是C++语 • 言后期引进的代码重用的模式,单一继承对应派生类上逆时 • 恒只有一个直接基类。多重继承打破了单一继承这种单枝独 • 上的简单性。 • C风格的类型转换对于系统级的操作是必不可少的。 • C++鉴于C风格的类型转换未提供足够明显的安全保障 • 机制,特引进新形式的类型变换规则。

  2. 五、新的类型变换

  3. 五、新的类型变换 • 英文cast可翻译成变换、映射或投影。~_cast 类型变 • 换有四种形式,格式如下: • dynamic_cast<ClassC*>(pObj) • static_cast< type >(expre ) • reinterpret_cast< type* >( pExpre ) • const _cast< type* >(pExpre ) • 其中尖括号中的类型指出表达式结果的类型,圆括号中 • 的操作数为待变换的数据。

  4. 1. dynamic_cast动态类型变换 • 动态类型变换dynamic_cast用于操作一个多态类的对 • 象指针以便进行上下映射。仅当基类存在至少一个虚函数 • 时,才可以启动动态类型变换机制。 • 设继承层次的类从上到下的关系为ClassB, ClassC, • ClassD, 对象指针变换的语法格式常用的为: • dynamic_cast<ClassC*>(pObj) • //不支持 dynamic _cast <float*>(pObj) • pObj是指向对象的指针,这个指针具有动态类型和静态 • 类型。如果pObj的动态类型和静态类型是派生类的类型 • ClassD*,表达式的结果是ClassC*类型。

  5. 如果pObj的静态类型是基类的类型ClassB*,表达式的如果pObj的静态类型是基类的类型ClassB*,表达式的 • 结果根据pObj的动态类型确定: • 如果pObj的动态类型是ClassC*型或ClassD*型的对象 • 指针,则表达式的结果是ClassC*类型; • 如果pObj的动态类型是ClassB*类型则表达式返回 • NULL。此种类型转换不支持将对象指针类型转换为算术指 • 针类型。 • 例如,不支持 dynamic _cast <int*>(pObj)。

  6. [例]动态类型变换 • #include <stdio.h> • class ClassB • { public: long m_n; • public: ClassB() {m_n=1;} • virtual f(){} • void Show() {printf ("ClassB::m_n=%d\t",m_n); } • class ClassC: public ClassB • { public: ClassC() {m_n=2;} • void Show() • { printf ("ClassC::m_n=%d\t",m_n); } • };

  7. class ClassD: public ClassC • { public:ClassD () {m_n=3; } • void Show() • { printf ("ClassD::m_n=%d\t",m_n); } • }; • void fcast (ClassB* pObj) • { • ClassC* pObjc=dynamic_cast<ClassC*>(pObj) ; • if (pObjc!=NULL) • pObjc->Show(); • }

  8. void main() • { ClassB objb; fcast (&objb); • ClassB *pObjb=new ClassD(); • //pObjb具有动态类型为ClassD* • fcast (pObjb); • ((ClassD*)pObjb)->Show(); • //将pObjb真正变换到原来的对象地址 • ClassC objc; • fcast (&objc); • } //输出: • ClassC::m_n=3 ClassD::m_n=3 ClassC::m_n=2

  9. 动态类型变换dynamic_cast也可操作一个多态类的对动态类型变换dynamic_cast也可操作一个多态类的对 • 象引用,以便将派生类对象或对象引用的实际值向上变换到 • 基类的引用。对象引用变换的语法格式为: • dynamic_cast<ClassC&>(rObj) • //不支持 static_cast <int&>(rObj) • rObj是对象引用。如果rObj的动态类型和静态类型都 • 是派生类的类型ClassD&,表达式的结果是ClassC&型的对 • 象引用。 • 如果rObj的静态类型是基类的类型ClassB&,而动态类 • 型是ClassC&或ClassD&类型,则表达式的结果是 • ClassC&类型; • 如果rObj的动态类型是ClassB&类型,表达式抛出异 • 常bad_cast。该形式需要typeinfo.h 头文件的支持。

  10. [例]引用形式的动态类型变换 • #include <stdio.h> • #include<typeinfo.h> • class ClassB • { public: long m_n; • public: ClassB() {m_n=1;} • virtual void Show() • { printf ("ClassB::m_n=%d\n",m_n); } • }; • class ClassC:public ClassB • { public: ClassC() { m_n=2; } • void Show() • { printf ("ClassC::m_n=%d\n",m_n); } • };

  11. class ClassD:public ClassC • { public:ClassD() { m_n=3; } • void Show () {printf ("ClassD::m_n=%d\n",m_n);} • ~ClassD() { printf ("~ClassD;\n"); } • }; • void fcast (ClassB& rObj) • { try{ ClassC& rObjc=dynamic_cast<ClassC&>(rObj) ; • rObjc.Show(); • } • catch (bad_cast) { printf ("bad_cast produced\n"); } • }

  12. void main() • { ClassB objb; • fcast (objb); • ClassB* pObjb=new ClassD(); • fcast (*pObjb); //动态绑定发生作用 • ((ClassD&)*pObjb). Show (); • ClassC objc; • fcast (objc); • delete (ClassD*)pObjb; • } 程序输出结果: bad_cast produced ClassD::m_n=3 ClassD::m_n=3 ClassC::m_n=2 ~ClassD;

  13. 动态类型变换可以在多继承的层次上进行。 • 最晚派生类对象地址可以向上沿任意分支投影。 • 当向上投影到到其中一个分支的基对象指针时,这个指 • 针可以借助动态类型变换间接地向上转换到另外的分支。 • 如果对象指针的当前动态值指向继承树下面的派生类对 • 象,则可以将其安全地变换到另一个基类的对象地址。 • 动态类型变换支持的是向上投影对象指针的实际值。

  14. 2. static_cast静态类型变换 • 静态类型变换可以在相关对象引用或对象指针之间进 • 行,也可以在内置数据类型之间进行。 • dynamic_cast类型转换要求基类存在至少一个虚函 • 数,静态类型变换不要求这一点。 • 对象指针指针变换的语法格式为: • static_cast <ClassC*>(pObj) • //不支持 static_cast <double*>(pObj) • pObj是指向类继承层次的对象的指针,不管这个指针具 • 有动态类型和静态类型,表达式的结果都强制性的转换为 • ClassC*型的对象指针。

  15. 对象引用变换的语法格式为: • static_cast <ClassC&>(rObj) • //不支持 static_cast <double&>(rObj) • rObj是类的继承层次的对象引用,不管这个引用动态类 • 型和静态类型如何,表达式的结果都强制性的转换为 • ClassC&型的对象引用。 • 对于对象别名的类型转换,编译器保留虚函数的动态绑 • 定机制。

  16. [例]继承层次的静态类型变换 • #include <stdio.h> • class ClassB • { public: long m_n; • public: ClassB() { m_n=1; } • //virtual // 去掉双斜杠Show()为虚函数 • void Show() { printf ("ClassB::m_n=%d\n",m_n);} • }; • class ClassC:public ClassB • { public: ClassC() { m_n=2; } • void Show() • { printf ("ClassC::m_n=%d\n",m_n); } • };

  17. class ClassD:public ClassC • { public:ClassD() {m_n=3;} • void Show() {printf ("ClassD::m_n=%d\n",m_n);} • ~ClassD() { printf ("~ClassD;\n"); } • }; • void fcastb (ClassB& rObj,int n) • { if (n==1) • { ClassC& rObjc=static_cast<ClassC&>(rObj) ; • rObjc.Show(); } • else { ClassC& rObjc= (ClassC&)(rObj) ; • rObjc.Show(); } • } //上面if分支两种变换格式效果是等价的

  18. void fcasta (ClassB* pObj,int n) • { ClassC* pObjc; • if (n==1) • pObjc=static_cast<ClassC*>(pObj) ; • else pObjc=(ClassC*)(pObj) ; • pObjc->Show(); • } • void fa () //显示指针类型静态变换 • { ClassB objb; fcasta(&objb,1); • ClassB* pObjb=new ClassD(); fcasta(pObjb,1); • ClassC objc; fcasta(&objc,1); • delete (ClassD*)pObjb; • }

  19. void fb () //显示引用形式静态类型变换 • { ClassB objb; fcastb(objb,1); • ClassB* pObjb=new ClassD(); • fcastb (*pObjb,2); • ClassC objc; fcastb(objc,1); • } • void main() { fa(); fb(); } • 静态类型变换也适应于内置数据类型的转换,即可以将 • double型的表达式转换为int型的表达式等,反之亦然。其 • 形式为: • static_cast< type >( expre ) • //类似于C形式的 (type) expre • 这个静态类型变换将expre类型转换为目标type类型。

  20. Show不是虚函数时 Show是虚函数时输出 • 输出如下结果: 结果如下: • ClassC::m_n=1 ClassB::m_n=1 • ClassC::m_n=3 ClassD::m_n=3 • ClassC::m_n=2 ClassC::m_n=2 • ~ClassD; ~ClassD; • ClassC::m_n=1 ClassB::m_n=1 • ClassC::m_n=3 ClassD::m_n=3 • ClassC::m_n=2 ClassC::m_n=2

  21. 3. reinterpret_cast 重新翻译变换 • 重新翻译变换的语法格式如下: • reinterpret_cast< type* >( pExpre ) • //相当于 (type*) pExpre • reinterpret_cast< type& >( LExpre ) • //相当于 (type&)LExpre • reinterpret_cast重新翻译变换是C风格类型转换 • (type*) pExpre或 (type&)LExpre的替代物。这个类型变换 • 允许任意指针类型pExpre转换为其它type*的指针类型,甚 • 至允许整型数与指针类型的相互转换。但编程时从不将整型 • 数据变换为地址表达式,因为这违反指针仅用于操作系统固 • 有的内存地址编号的规则。

  22. reinterpret_cast也允许左值表达式LExpre转换为其它 • 的引用类型。 • 前面的动态类型变换和静态类型变换要求类的继承层次 • 之间的上下转换,不能将int*的指针变换成long*型的指针, • 而这里的reinterpret_cast则支持这种形式的变换。 • reinterpret_cast不支持(type)expre风格的数据类型转 • 换,例如不能将int的表达式变换成double型的数据,而 • static_cast则支持这种C-style cast 类型的转换。

  23. [例]简单变量的指针和引用变换 • #include <stdio.h> • void f(short& v) { printf ("%d\t",v);} • void f(double* p) { printf ("%f\t",*p);} • void main() • { long l=10; float tf =20.0f; • f (reinterpret_cast<short&>(l)); • f (reinterpret_cast<double*>(&tf)); • f ((short&) l ); • printf ("*((double*)&tf)=%f\t",*((double*)&tf)); • f ((double*)(&tf)); • } //////////////程序运行输出结果:///////////////////// • 10 0.000000 10 *((double*)&tf)=0.000000 0.000000

  24. 4. const_cast 松动const属性变换 • 形如void f(type *p)的函数接口是一个普通指针形参, • 普通指针不可以指向由定义语句[const type v;]定义的不变 • 量v,const_cast在变换时可以松动不变量的const属性。 • 通常普通指针形参本可以声明为只读指针形参 • void f (const type *p),但由于程序员的疏忽而遗漏了 • const关键字,这种情况下const_cast变换便能有效的发挥 • 作用。

  25. const_cast的语法格式为: • const _cast< type* >( pExpre ) • //相当于 (type*) pExpre • const _cast< type& >( LExpre ) • //相当于 (type&)vExpre • 其中pExpre是const type*的指针表达式,vExpre是 • const type的表达式。也就是源操作数具有const的只读性 • 质,而目标操作数是同类型的没有const的表达式。 • const _cast 变换将只读的表达式变换成可变的形式。 • 这种变换只应该是形式上的,即原来的只读数据源不应因为 • 变换的结果而遭到非预期的攻击。

  26. [例]松动实参const属性的变换 • #include <stdio.h> • void fc (int* p) { printf ("%d\t",*p); } • void fc (int& v) { printf ("%d\n",v); } • void main() • { const int d=20; • fc ( const_cast<int*>(&d)); • fc ( const_cast<int&>(d)); • } //输出结果:20 20 • void fc(int* p) { printf (“%d\t”,*p); }本可以实质地改为 • void f(const int* p) { printf (“%dt”,*p);},函数fc(int*)不能 • 匹配只读指针实参,而函数f(const int*)可安全地匹配int*型 • 的实参。

  27. 5. typeid关键字 • 运行时类型识别(RTTI)信息是一种编程的重要标志,它 • 允许变量的类型在运行期间确定。 • 对于typeid运算符而言,表达式的类型分两种:一种是 • type型的变量,另一种是type*型的指针。type&型的引用参 • 照type型的变量,type*&型的引用参照type*型的指针。 • typeid关键字可以用来求出表达式的类型信息type或 • type*。 • 对于多态类则可以进一步求出表达式的动态类型信息, • 此时应打开/GR开关。

  28. a. typeid关键字的用法 • typeid关键字的用法有点类似于sizeof 运算符。Typeid • 函数用于确定表达式或某一数据的类型,其语法格式为: • const class type_info typeid (expression) • const class type_info typeid (type) • type 是定义变量的类型名, expression 是表达式。 • class type_info是在头文件<typeinfo.h>中声明的类, • typeid函数就返回这个类的const对象。下面表达式的结果 • 为真。 • typeid(1.0f) ==typeid(float) typeid(1.0) ==typeid(double) • typeid(1) ==typeid(int) typeid('1')==typeid(char) • double d; typeid(&d) ==typeid(double*)

  29. [例] typeid显示表达式的类型信息 • #include <stdio.h> • #include<typeinfo.h> • class ClassB //多态类之根基类 • { public: virtual void SetB() {m_b=1;} • int m_b; • }; • class ClassC : public ClassB //多态类继承体系之派生类 • { public: void SetB() { m_c=2;} int m_c; }; • void main() • { double d; • if (typeid (int) !=typeid (double*))

  30. if (typeid(&d) ==typeid(double*)) • printf("%s\t", typeid(double*).name()); • If (typeid('1')==typeid(char)) • printf ("%s\t", typeid('1').name()); • long n; • float& rn=(float&)n; • printf ("%s,\t%s\t", typeid(n).name(),typeid(rn).name()); • ClassB b; ClassC c; • ClassB& rb=c; • printf ("%s,\t%s\t", typeid(b).name(),typeid(rb).name()); • } //输出: • double* char long, float class ClassB, class ClassC

  31. b. type_info类 • VC6.0中type_info类的声明如下: • class type_info • { public: • virtual ~type_info(); • int operator==(const type_info& rhs) const; • int operator!=(const type_info& rhs) const; • int before(const type_info& rhs) const; • const char* name() const; • const char* raw_name() const;

  32. private: • void *_m_data; • char _m_d_name[1]; • type_info(const type_info& rhs); • type_info& operator=(const type_info& rhs); • };

  33. [例] before和raw_name()成员函数 • #include <stdio.h> • #include<typeinfo.h> • #include<string.h> • class ClassB • { public: • virtual void SetB() { m_b=1; } • int m_b; • }; • class ClassC : • public ClassB • { public: int m_c; void SetB(){ m_c=2; } • };

  34. void f1(int,float){} • void f2(ClassC*,long){} • void main() • { double d=1; • const type_info& t1=typeid(f1); • const type_info& t2=typeid(f2); • printf ("%s,\t%s\n", t1.name(),t1.raw_name()); • printf ("%s,\t%s\n", t2.name(),t2.raw_name()); • ClassC c; ClassB& rb=c; • const type_info& tc=typeid(ClassC*); • // typeid(e)是const对象,别名也是const属性

  35. const type_info& tb=typeid(rb); • if (t1.before(t2)) • printf("1--t1.before(t2)\t"); • If (strcmp (t1.raw_name(),t2.raw_name())<0) • printf ("2--t1.before(t2)\n"); • If (tb.before(tc)) • printf("3--%s,\t%s\t", tb.name(),tc.name()); • if (strcmp (tb.raw_name(),tc.raw_name())<0) • printf ("4--%s,\t%s\n", tb.name(),tc.name()); • void* vp=&d; • printf ("%s,\t%s\n", typeid(vp).name(),typeid • (*(double*)vp).name()); • }

  36. ///////////////输出结果:///////////////////// • void (__cdecl*)(int,float), .P6AXHM@Z • void (__cdecl*)(class ClassC *,long), • .P6AXPAVClassC@@J@Z • 1--t1.before(t2) 2--t1.before(t2) • 3--class ClassC, class ClassC * • 4--class ClassC, class ClassC * • void *, double

  37. 说明: • 成员函数before的排序算法在编译器内部通过strcmp • 函数相仿的机制进行,在微软VC中根据编码后的字符串 • raw_name()进行操作。这样表达式tb.before(tc)在效果上 • 与strcmp(tb.raw_name(),tc.raw_name())<0是相当的。 • 其它的编译器可能将字符串name()作为排序的标准。 • vp的类名是void*,但vp的间接变量*vp是无效的访问指针形 • 式, typeid(*vp)导致错误提示。 • 将这个指针予以复原,然后再间接访问复原的指针 • *(double*)vp则得到double型的表达式,输出的结果表明这 • 一点。

  38. 请打开“第23章(1).ppt”

More Related