多态是面向对象理论的三大支柱之一_第1页
多态是面向对象理论的三大支柱之一_第2页
多态是面向对象理论的三大支柱之一_第3页
多态是面向对象理论的三大支柱之一_第4页
多态是面向对象理论的三大支柱之一_第5页
已阅读5页,还剩28页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

1、C/C+程序设计程序设计1 多态是面向对象理论的三大支柱之一。多态是面向对象理论的三大支柱之一。 多态是不能凭空独立的支柱,需要另外两个支多态是不能凭空独立的支柱,需要另外两个支柱构成三足鼎立之势柱构成三足鼎立之势,另外两个支柱是封装和继承。另外两个支柱是封装和继承。 封装是数据和算法的抽象描述,继承是关于抽封装是数据和算法的抽象描述,继承是关于抽象描述的演化发展,多态则是对于程序流程前瞻性象描述的演化发展,多态则是对于程序流程前瞻性的刻画。的刻画。 简单的说多态是基对象的指针调用派生类的虚简单的说多态是基对象的指针调用派生类的虚函数。函数。C/C+程序设计程序设计2C/C+程序设计程序设计3

2、五、名称索引的优先级五、名称索引的优先级 在类上下继承层次中使用成员名称有两种方式:一种是在类上下继承层次中使用成员名称有两种方式:一种是有意模棱两可的有意模棱两可的(implicit)隐约索引。即不带类域分辨符方式隐约索引。即不带类域分辨符方式的索引如:的索引如:Draw(x,y),Draw 是成员函数的函数名。是成员函数的函数名。 另一种是一清二楚另一种是一清二楚(explicit)的带类域分辨符方式的全限的带类域分辨符方式的全限定索引如:定索引如:CBase:Draw(x,y),严格全限定索引在避免函数,严格全限定索引在避免函数的递归调用是必须的。的递归调用是必须的。 隐约索引的目的一方

3、面是减少代码书写量,另一方面替隐约索引的目的一方面是减少代码书写量,另一方面替虚拟函数的隐约调用铺平道路。虚拟函数的隐约调用铺平道路。 在成员函数中出现的名称编译器根据下面的次序搜寻:在成员函数中出现的名称编译器根据下面的次序搜寻: C/C+程序设计程序设计4 1. 首先编译器在当前的内层块范围搜寻,由内向外一直首先编译器在当前的内层块范围搜寻,由内向外一直到最外层的函数范围,包括函数的形参和隐约名称如到最外层的函数范围,包括函数的形参和隐约名称如m_n或或者如者如Draw(x,y)。 2. 编译器在当前编译器在当前(最晚最晚)派生的类中搜寻所出现的名称。派生的类中搜寻所出现的名称。 3. 如

4、果在当前类中未搜寻到指定的隐约名称,则上溯如果在当前类中未搜寻到指定的隐约名称,则上溯到直接基类中搜寻直到根基类。到直接基类中搜寻直到根基类。 4. 然后在全局范围内搜寻。然后在全局范围内搜寻。 5. ClassX:m_f 或或this-ClassX:m_f或或:g_f的全限定的全限定名方式清晰地强制编译器搜寻指定的唯一名称。分辨符名方式清晰地强制编译器搜寻指定的唯一名称。分辨符:左左边的名称是边的名称是class,struct,union或或enum引入的类型名。引入的类型名。 6.如果名称前有关键字如果名称前有关键字class,struct和和union,强制编译,强制编译器搜寻它们引入的

5、数据类型名。器搜寻它们引入的数据类型名。 C/C+程序设计程序设计5 对于对于obj.m_n 或或pobj- Draw(x,y)访问格式,如果派生访问格式,如果派生类没有提供相关的成员,则使用从基类继承的可访问成员。类没有提供相关的成员,则使用从基类继承的可访问成员。如果派生类中存在相应的成员优先采用派生类的覆盖成员。如果派生类中存在相应的成员优先采用派生类的覆盖成员。 这称为优先采用派生类成员名称的支配原则。这称为优先采用派生类成员名称的支配原则。 对于虚函数,对象指针的动态类型确定继承树层次搜寻对于虚函数,对象指针的动态类型确定继承树层次搜寻的入口点,对于非虚函数对象指针的静态类型确定继承

6、树层的入口点,对于非虚函数对象指针的静态类型确定继承树层次的切入点。次的切入点。 如果两者的类型都是如果两者的类型都是ClassX*,则以,则以ClassX作为搜寻的作为搜寻的入口点,向上搜寻指定的成员直到最快找到指定的成员为入口点,向上搜寻指定的成员直到最快找到指定的成员为止。止。C/C+程序设计程序设计6六、六、 虚函数动态绑定调用算例虚函数动态绑定调用算例 基类的虚函数是公共的,派生类的覆盖版本可基类的虚函数是公共的,派生类的覆盖版本可以是私有的。以是私有的。 这样仅允许基对象指针在上层动态调用派生类这样仅允许基对象指针在上层动态调用派生类的虚函数,而不由对象直接外部访问,这保证虚函的虚

7、函数,而不由对象直接外部访问,这保证虚函数族入口的唯一性。数族入口的唯一性。C/C+程序设计程序设计7例例 关键字关键字virtual的开关作用和隐约调用的开关作用和隐约调用#include class ClassX public: virtual void F(); private: long m_nx; ; void ClassX:F() printf (ClassX=%d,sizeof (ClassX); class ClassY:public ClassX private: long m_ny; void F(); ; C/C+程序设计程序设计8void ClassY:F() prin

8、tf(ClassY=%d,sizeof(ClassY); void Showv (ClassX objx) objx.F(); void Show (ClassX* pBase) pBase-F(); void Show (ClassX& r) r.ClassX:F(); void main () ClassX* pobjx=new ClassX(); ClassY objy; int k=1; scanf (%d,&k); if (k=1) Show (pobjx); Show (&objy); else Show (*pobjx); Show(objy); Showv (objy); C/

9、C+程序设计程序设计9说明:说明: 在在ClassX类中成员函数类中成员函数F()前带关键字前带关键字virtual且且k=1输出输出结果:结果: ClassX=8,ClassY=12, ClassX=8, 当当k=2输出输出: ClassX=8,ClassX=8, ClassX=8, 在在ClassX类中成员函数类中成员函数F()前不带关键字前不带关键字virtual即去掉即去掉virtual时程序输出结果:时程序输出结果: ClassX=4, ClassX=4, ClassX=4,C/C+程序设计程序设计10 类中函数声明中的前置关键字类中函数声明中的前置关键字virtual对于成员函数其

10、着对于成员函数其着动静绑定的切换作用,在成员函数的调用点动静绑定的切换作用,在成员函数的调用点 void Show(ClassX* pBase) pBase-F(); pBase-F(); 编译器扫描声明部分,如果得知编译器扫描声明部分,如果得知F是非虚函数则进行静是非虚函数则进行静态联编,即根据态联编,即根据pBase的静态类型进行函数的调用匹配,的静态类型进行函数的调用匹配,pBase的静态类型就是形参列表中定义的类型即的静态类型就是形参列表中定义的类型即ClassX*,因此因此pBase-F();的隐约调用转换为全限定名的显式调用的隐约调用转换为全限定名的显式调用pBase-ClassX

11、:F();,这就是静态联编。,这就是静态联编。 C/C+程序设计程序设计11 不管程序运行过程中虚实结合时不管程序运行过程中虚实结合时pBase得到何值,调用得到何值,调用的总是早诞生的成员函数版本。的总是早诞生的成员函数版本。上面多次调用中上面多次调用中pBase得到的对象指针动态值是不同的,但得到的对象指针动态值是不同的,但一律执行基类的版本。一律执行基类的版本。 如果探查到如果探查到Show是虚函数则进行动态绑定,即根据是虚函数则进行动态绑定,即根据pBase的动态类型进行函数的调用匹配。的动态类型进行函数的调用匹配。 如果虚实结合时如果虚实结合时pBase的动态类型为的动态类型为Cla

12、ssY*,则调用,则调用虚函数虚函数ClassY:F()。pBase的动态类型为的动态类型为ClassX*,则调用,则调用虚函数虚函数ClassX:F()。C/C+程序设计程序设计12例例 静态联编调用非虚成员函数,派生类的覆盖版本是公静态联编调用非虚成员函数,派生类的覆盖版本是公共的共的#include class ClassX long m_nx; public: void F(); ; class ClassY:public ClassX long m_ny; public: void F(); ; void ClassX:F() printf ( ClassX=%d,sizeof (C

13、lassX); void ClassY:F() printf (ClassY=%d,sizeof (ClassY); enum classx,classy ;C/C+程序设计程序设计13void Show (ClassX* pBase,int morph) switch (morph) case classx: pBase-F();break; case classy: (ClassY*)pBase) -F ();break; void main() Show (new ClassX(),classx); Show (new ClassY,classy); /输出:输出:ClassX=4, C

14、lassY=8, C/C+程序设计程序设计14 多态类对象内存的代价是明显的,即相应于非多态类的多态类对象内存的代价是明显的,即相应于非多态类的对象增添对象增添4个字节的内存空间,从个字节的内存空间,从4,8对应地增至对应地增至8,12。 获得的好处就是只需简单的写一条隐约调用的语句,编获得的好处就是只需简单的写一条隐约调用的语句,编译器替程序员作了许多常规的分支判断选择。译器替程序员作了许多常规的分支判断选择。 C/C+程序设计程序设计15例例 对象引用的隐含类型转换作用对象引用的隐含类型转换作用# includeclass CIo public:char obuffer48;class C

15、Stream : public CIo public:CStream& operator(double); ;class CWithassign : public CStream public: ;CStream& operator(CStream& r,int n) static const char* fmt = operator( CStream&, %d); sprintf (r.obuffer,fmt,n); printf (%s,r.obuffer); return r;C/C+程序设计程序设计16CStream& CStream:operator(double d) static

16、 const char* fmt = CStream:operator (double d=%f);n; sprintf (obuffer,fmt,d); printf (n%s,obuffer); return *this;void main () CWithassign wout; operator(wout,1); wout23; wout4.0; CIo cio; operator(CStream&) cio,5); (CStream&) cio67; C/C+程序设计程序设计17/程序运行输出结果程序运行输出结果: operator(CStream&,1); operator( CS

17、tream&,2); operator( CStream&,3); CStream:operator(double d=4.0000000); operator(CStream&,5); operator( CStream&,6); operator( CStream&,7); 引用形式强制类型转换的含义是根据目标类型描述的数引用形式强制类型转换的含义是根据目标类型描述的数据结构和算法操作源对象的内存数据。据结构和算法操作源对象的内存数据。 本题中对象数据由根基类中声明,派生类提供算法,算本题中对象数据由根基类中声明,派生类提供算法,算法在内存中不属于对象,因此对象引用的上下映射在内存的法在内

18、存中不属于对象,因此对象引用的上下映射在内存的角度看是一致的。角度看是一致的。 C/C+程序设计程序设计18 函数调用函数调用operator(wout,1) 可以等价地写为可以等价地写为 operator(CStream&)wout,1), 在接口处对应着在接口处对应着CStream& r=wout的继承层次上下间对象的继承层次上下间对象引用隐含类型转换。引用隐含类型转换。 简洁的调用形式简洁的调用形式wout23; wout4.0;也采用向上也采用向上映射的默许规则。映射的默许规则。 函数调用函数调用operator(CStream&) cio,5)采用向下映射采用向下映射形式的强制类型转

19、换,含义为基对象借用派生类的算法操作形式的强制类型转换,含义为基对象借用派生类的算法操作基对象的内存数据。基对象的内存数据。 隐含类型转换对于非虚成员函数不起动态绑定的作用。隐含类型转换对于非虚成员函数不起动态绑定的作用。 C/C+程序设计程序设计19 对于对于ostream类库的运算符重载函数类库的运算符重载函数operator运算符重载函数而言,这一默许运算符重载函数而言,这一默许的类型转换机制有利于它们被派生类对象隐含地继承调用,的类型转换机制有利于它们被派生类对象隐含地继承调用,提供统一的接口而无须强制类型转换。提供统一的接口而无须强制类型转换。 sprintf 函数是格式化转换函数,

20、函数是格式化转换函数,sprintf函数的目的地函数的目的地是一个字符缓冲区,其变换规则与是一个字符缓冲区,其变换规则与printf 函数相同。函数相同。格式为:格式为: int sprintf (char *buffer, const char * format, variable_list); sprintf返回实际写入返回实际写入buffer数组的字符个数。数组的字符个数。C/C+程序设计程序设计20七、虚拟析构函数七、虚拟析构函数 构造函数本身决不作为虚函数。构造函数本身决不作为虚函数。 构造函数中可以调用虚函数,但这个虚函数不起向下的构造函数中可以调用虚函数,但这个虚函数不起向下的动

21、态绑定的作用。动态绑定的作用。 对于存在虚函数的类,将析构函数声明为虚拟的成员函对于存在虚函数的类,将析构函数声明为虚拟的成员函数是至关重要的,这样可以保证内存的泄漏降至最少。数是至关重要的,这样可以保证内存的泄漏降至最少。 在基类的析构函数前加上关键字在基类的析构函数前加上关键字virtual声明,则该析构声明,则该析构函数就成为虚析构函数,派生类的析构函数也跟着成为虚析函数就成为虚析构函数,派生类的析构函数也跟着成为虚析构函数,派生类的析构函数前可以明显地前置构函数,派生类的析构函数前可以明显地前置virtual声明,声明,也可以隐含的省去不写。也可以隐含的省去不写。C/C+程序设计程序设

22、计21例例 虚析构函数在动态绑定中的重要性虚析构函数在动态绑定中的重要性#include class ClassX public: virtual ClassX()printf(ClassX();); protected: ClassX() printf(ClassX();); ; class ClassY:public ClassX public: ClassY (int n=1) m_n=n; m_p=new long m_n; printf (ClassY();); private: ClassY() delete m_p; printf (ClassY();); long m_n; l

23、ong* m_p; ; C/C+程序设计程序设计22void main() ClassX* pobjx= new ClassY(); delete pobjx; 当基类析构函数是虚函数时程序输出结果:当基类析构函数是虚函数时程序输出结果: ClassX(); ClassY(); ClassY(); ClassX();去掉基类析构函数前的关键字去掉基类析构函数前的关键字virtual输出结果:输出结果: ClassX(); ClassY(); ClassX();C/C+程序设计程序设计23 new ClassY()导致系统先调用导致系统先调用new运算符函数,运算符函数,new运运算符函数调用的

24、结果如果成功,则继续调用构造函数算符函数调用的结果如果成功,则继续调用构造函数ClassY(int),派生类的构造函数诱发自基类构造函数起从上,派生类的构造函数诱发自基类构造函数起从上到下的连续序列调用。到下的连续序列调用。 delete 的调用次序与的调用次序与new运算符相反,运算符相反,delete首先调用首先调用new运算符诞生的运算符诞生的heap对象所隶属的析构函数,然后释放对象所隶属的析构函数,然后释放new运算符动态获取的内存空间。运算符动态获取的内存空间。 但此处由于派生类对象指针向上映射的结果,但此处由于派生类对象指针向上映射的结果,new ClassY()诞生的对象是派生

25、类的对象,但接管该对象的指针诞生的对象是派生类的对象,但接管该对象的指针则是基对象指针,这是编译器暗许的类型转换。则是基对象指针,这是编译器暗许的类型转换。C/C+程序设计程序设计24对于此种指针动静类型不一致的情形:对于此种指针动静类型不一致的情形: ClassX* pobjx= new ClassY(); delete pobjx; delete pObjx激发的析构函数的调用就根据析构函数的激发的析构函数的调用就根据析构函数的虚拟与否进行选择:虚拟与否进行选择: 1. 析构函数若是虚拟的,则析构函数若是虚拟的,则pobjx的动态类型决定调用的动态类型决定调用ClassY类的析构函数类的析

26、构函数 这实际上也就是保证这实际上也就是保证 delete pobjx 能够暗中正确的还能够暗中正确的还原为原为delete pobjy,以便和,以便和pobjx=new ClassY()遥相呼应。遥相呼应。 2. 析构函数是非虚拟的,则析构函数是非虚拟的,则pobjx的静态类型决定调用的静态类型决定调用ClassX类的析构函数类的析构函数C/C+程序设计程序设计25八、八、 纯虚函数和抽象类纯虚函数和抽象类 多态是用基对象的指针调用派生类的虚函数,此种调用多态是用基对象的指针调用派生类的虚函数,此种调用在基对象指针静态赋值指向距离顶层基类较远的派生类对象在基对象指针静态赋值指向距离顶层基类较

27、远的派生类对象时,容易导致上层基类虚函数形成死码的局面。时,容易导致上层基类虚函数形成死码的局面。 死码是程序运行时处理器不能到达的指令。于是提出一死码是程序运行时处理器不能到达的指令。于是提出一个明显的指标,提醒基类的虚函数应该轻灵短小。个明显的指标,提醒基类的虚函数应该轻灵短小。C/C+程序设计程序设计26 最空灵的虚函数莫过于一无所有的虚函数,以一个最空灵的虚函数莫过于一无所有的虚函数,以一个=0来标志这样的虚函数如下:来标志这样的虚函数如下:class ClassA public: virtual ClassA ()=0 virtual int vPureFunct (.)=0; vi

28、rtual int PureFunct ()=0 return 1; int m_a; ; C/C+程序设计程序设计27 在基类的声明格式中以在基类的声明格式中以=0作标志的虚函数称为纯虚函作标志的虚函数称为纯虚函数。纯虚函数一般不带实现部分,期待派生类提供覆盖版数。纯虚函数一般不带实现部分,期待派生类提供覆盖版本;纯虚函数在定义的时候也可以将空函数作为函数部分,本;纯虚函数在定义的时候也可以将空函数作为函数部分,这在纯虚析构函数是必要的。这在纯虚析构函数是必要的。 对于多态类析构函数通常是虚拟函数,一般不作为纯虚对于多态类析构函数通常是虚拟函数,一般不作为纯虚函数。函数。 将至少存在一个纯虚

29、函数的类称为抽象类。抽象类旨在将至少存在一个纯虚函数的类称为抽象类。抽象类旨在为多态类提供统一的无负担的接口为多态类提供统一的无负担的接口(尽量避免死码的出现尽量避免死码的出现),而接口的有份量的激活的代码由派生类覆盖版本提供。而接口的有份量的激活的代码由派生类覆盖版本提供。 抽象类的特点是不能用其定义对象,即抽象类的特点是不能用其定义对象,即 ClassA *pObja=new ClassA();和和 ClassA obja; 是非法的定义语句。是非法的定义语句。C/C+程序设计程序设计28 以上这种抽象类无实例的概念渗透到下面几个层面:以上这种抽象类无实例的概念渗透到下面几个层面: 1.

30、无抽象类的对象即无无抽象类的对象即无 ClassA obja但可以有抽象类的但可以有抽象类的对象指针如对象指针如ClassA* pObja; 2. 无抽象类的数值形参即无无抽象类的数值形参即无f(ClassA a)但可以有抽象类但可以有抽象类的引用形参如的引用形参如f(ClassA& r); 3. 不返回抽象类的对象即无不返回抽象类的对象即无 ClassA f() 到可以返回抽象到可以返回抽象类的指针如类的指针如ClassA* f(); 抽象类的构造函数不宜调用纯虚函数,无论直接或间抽象类的构造函数不宜调用纯虚函数,无论直接或间接,否则结果是不确定的。由于不存在抽象类的对象,因此接,否则结果是

31、不确定的。由于不存在抽象类的对象,因此抽象类基对象指针由具体派生类对象的地址初始化,抽象类抽象类基对象指针由具体派生类对象的地址初始化,抽象类的构造函数适宜处理为保护的访问控制属性,由派生类构造的构造函数适宜处理为保护的访问控制属性,由派生类构造函数调用。函数调用。C/C+程序设计程序设计29 抽象类常作为多态类的基类,在抽象基类派生的类如果抽象类常作为多态类的基类,在抽象基类派生的类如果出现了全不等于出现了全不等于0的虚函数的覆盖版本,称为具体类。的虚函数的覆盖版本,称为具体类。 抽象类的直接基类可以是具体类,具体类的行为不受其抽象类的直接基类可以是具体类,具体类的行为不受其后派生的抽象类的影响。抽象类的位置和其它类一样是非常后派生的抽象类的影响。抽象类的位置和其它类一样是非常灵活的。灵活的。 如如MFC类中一个至关重要的纯虚函数类中一个至关重要的纯虚函数OnDraw就是首度就是首度在视图类在视图类CView中亮相的,因此程序员必须在中亮相的,因此程序员必须在CView之下派之下派生类如生类如CUserView中提供不等于中提供不等于0的的OnDraw实现。实现。 每一次窗口重绘,应用程

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论