




已阅读5页,还剩53页未读, 继续免费阅读
版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
第8章 多态性 第8章 多态性 8.1 多态性概述 8.2 运算符重载 8.3 虚函数 8.4 抽象类 第8章 多态性 8.1 多态性概述 通俗地说,多态性是指用一个相同的名字定义不同的函数, 这些函数的执行过程不同,但是有相似的操作,即用同样的接 口访问不同的函数。 面向对象的多态性从实现的角度来讲,可以分为静态多态性 和动态多态性两种。静态多态性是在编译的过程中确定同名操 作的具体操作对象的,而动态多态性则是在程序运行过程中动 态地确定操作所针对的具体对象的。这种确定操作具体对象的 过程就是联编(binding),也称为绑定。联编是指计算机程序自身 彼此关联的过程。也就是把一个标识符名和一个存储地址联系 在一起的过程。用面向对象的术语讲,就是把一条消息和一个 对象的方法相结合的过程。 第8章 多态性 所谓消息,是指对类的成员函数的调用。不同的方法是指不同的实 现,也就是调用了不同的函数。按照联编进行阶段的不同,联编方法可 以分为两种:静态联编和动态联编。这两种联编过程分别对应着多态的 两种实现方式。联编工作在编译连接阶段完成的情况称为静态联编。在 编译、连接过程中,系统就可以根据类型匹配等特征确定程序中操作调 用与执行该操作的代码的关系,即确定某一个同名标识到底是要调用哪 一段程序代码。函数重载和运算符重载就属于静态多态性。 和静态联编相对应,如果联编工作在程序运行阶段完成,则称为动 态联编。在编译、连接过程中无法解决的联编问题,要等到程序开始运 行之后再来确定。例如,本章将要介绍的虚函数就是通过动态联编完成 的。 函数重载在函数及类的章节中曾做过详细的讨论,所以在本章中,静 态多态性主要介绍运算符重载;对于动态多态性,将对虚函数作详细介 绍。 第8章 多态性 8.2 运算符重载 C+中预定义的运算符的操作对象只能是基本数据类型。实际上,对于很多用户自 定义的类型(如类),也需要有类似的运算操作。例如点类point。 class point private: int x , y; public:/构造函数 point ( int xx=0,int yy =0) x = xx ; y = yy ; int get_x();/显示x值 int get_y();/显示y值 /. ; 第8章 多态性 声明点类的对象: point p1(1,1) , p2(3,3) 如果我们需要对p1和p2进行加法运算,如果写出表达式 “p1+p2”,编译时却会出错,因为编译器不知道该如何完成这个加 法。这时我们就需要编写程序来说明“+”怎样作用于point类对象时 ,该实现什么样的功能,这就是运算符重载。运算符重载是对已 有的运算符赋予多重含义,使同一个运算符作用于不同类型的数 据时,导致不同类型的行为。 在运算符重载的实现过程中,首先把指定的运算表达式转化 为对运算符函数的调用,运算对象转化为运算符函数的实参,然 后,根据实参的类型来确定需要调用的函数。这个过程是在编译 过程中完成的。 第8章 多态性 8.2.1 运算符重载的规则 运算符重载时必须要遵守一定的规则。 C+中的运算符除了少数几个(类属关系运算符“.”、作用域分辨 符“:”、成员指针运算符“*”、sizeof运算符和三目运算符“?:”)之外 ,全部可以重载,而且只能重载C+中已有的运算符,不能臆造新的 运算符。 重载之后运算符的优先级和结合性都不能改变,也不能改变运算 符的语法结构,即单目运算符只能重载为单目运算符,双目运算符只 能重载为双目运算符。 运算符重载后的功能应当与原有功能相类似。 重载运算符含义必须清楚,不能有二义性。 运算符的重载形式有两种:重载为类的成员函数和重载为类的友 元函数。 第8章 多态性 运算符重载为类的成员函数的一般语法形式如下: operator (形参表) 函数体; 运算符重载为类的友元函数的一般语法形式如下: friend operator (形参表) 函数体; 第8章 多态性 其中: 函数类型指定了重载运算符的返回值类型,也就是运算结果类型。 operator是定义运算符重载函数的关键字。 运算符是要重载的运算符名称。 形参表给出重载运算符所需要的参数和类型。 friend是对于运算符重载为友元函数时,在函数类型说明之前使用的 关键字。 特别需要注意的是,当运算符重载为类的成员函数时,函数的参数个 数比原来的操作数个数要少一个(后置“+”、“-”除外);当重载为类的友 元函数时,参数个数与原操作数的个数相同。原因是重载为类的成员函数 时,如果某个对象使用重载了的成员函数,自身的数据可以直接访问,就 不需要再放在参数表中进行传递,少了的操作数就是该对象本身。 第8章 多态性 8.2.2 运算符重载为成员函数 运算符重载实质上就是函数重载,当运算符重载为成员函数 之后,它就可以自由地访问本类的数据成员了。实际使用时,总 是通过该类的某个对象来访问重载的运算符。如果是双目运算符 ,一个操作数是对象本身的数据,由this指针指出,另一个操作数 则需要通过运算符重载函数的参数表来传递;如果是单目运算符 ,操作数由对象的this指针给出,就不再需要任何参数。下面分别 介绍这两种情况。 1双目运算:oprdl B oprd2 对于双目运算符B,如果要重载B为类的成员函数,使之能够 实现表达式oprdl B oprd2(其中oprdl为A类的对象),则应当把B重 载为A类的成员函数,该函数只有一个形参,形参的类型是oprd2 所属的类型。经过重载之后,表达式oprdl B oprd2就相当于函数 调用oprdl.operator B (oprd2)。 第8章 多态性 2单目运算 1)前置单目运算:U oprd 对于前置单目运算符U,如“-”(负号)、“+”等,如果要重载U为类 的成员函数,用来实现表达式U oprd(其中oprd为A类的对象),则U 应当重载为A类的成员函数,函数没有形参。经过重载之后,表达 式U oprd相当于函数调用oprd.operator U()。 例如,前置单目运算符“+”重载的语法形式如下: operator+(); 使用前置单目运算符“+”的语法形式如下: +; 第8章 多态性 2) 后置单目运算:opr dV 再来看后置运算符V,如“+”和“-”,如果要将它们重载为类的 成员函数,用来实现表达式oprd+或oprd-(其中oprd为A类的对象 ),那么运算符就应当重载为A类的成员函数,这时函数要带有一 个整型(int)形参。重载之后,表达式oprd+和oprd-就相当于函数 调用oprd.operator+(0)和oprd.operator-(0)。 例如,后置单目运算符“+”重载的语法形式如下: operator+(int); 使用后置单目运算符“+”的语法形式如下: +; 第8章 多态性 在C+中,可以通过在运算符函数参数表中是否插入关键字int来区 分前缀和后缀这两种方式。 对于前缀方式+ob,可以用运算符函数重载为 ob.operator +(); / 成员函数重载 或 operator + (X / 友元函数重载, 其中ob为类X对象的引用 对于后缀方式ob+,可以用运算符函数重载为 ob.ooperator +(int); / 成员函数重载 或 operator+(X / 友元函数重载 在调用后缀方式的函数时,参数int一般被传递给值0。(例 test_operator.cpp) 第8章 多态性 【例8-1】双目运算符重载为成员函数例题。/test_operator.cpp #include class point private: float x,y; public: point(float xx=0,float yy=0) x = xx ; y = yy; float get_x()return x; float get_y()return y; point operator+(point p1); point operator-(point p1); /重载运算符“+”和“-”为成员函数 ; point point:operator+(point q) return point(x+q.x,y+q.y); point point:operator-(point q) return point(x-q.x,y-q.y); void main() point p1(3,3),p2(2,2),p3,p4 p3=p1+p2; /两点相加 p4=p1-p2; /两点相减 cout class point private: float x,y; public: point(float xx=0,float yy=0)x=xx;y=yy; float get_x()return x; float get_y()return y; point operator+(); /重载前置运算 符“+” point operator-();/重载前置运算 符“-” ; point point:operator+() if(x0) -x; if(y0) -y; return *this; 第8章 多态性 void main() point p1(10,10),p2(200,200); /声明point类的对象 for(int i=0;i”。 第8章 多态性 1双目运算:oprdl B oprd2 对于双目运算符B,如果opr dl为A类的对象,则应当把B重载为A类的友 元函数,该函数有两个形参,其中一个形参的类型是A类。经过重载之后 ,表达式oprdl B oprd2就相当于函数调用operator B(opr dl,opr d2)。 2单目运算 1)前置单目运算:U oprd 对于前置单目运算符U,如“-”(负号)等,如果要实现表达式U oprd(其中 oprd为A类的对象),则U可以重载为A类的友元函数,函数的形参为A类的 对象。经过重载之后,表达式U oprd相当于函数调用operator U(oprd)。 2)后置单目运算:oprd V 对于后置运算符V,如“+”和“-”,如果要实现表达式oprd +或oprd -( 其中oprd为A类的对象),那么运算符就可以重载为A类的友元函数,这 时函数的形参有两个,一个是A类的对象oprd,另一个是整型(int)形参。 重载之后,表达式oprd+和oprd-就相当于函数调用operator+(oprd,0)和 operator-(oprd,0)。 第8章 多态性 【例8-3】双目运算符重载为友元重载例题。 friend point operator+(point p1,point p2) /重载运算符“+” friend point operator-(point p1,point p2) /和“-”为友元函数 point operator+(point p1,point p2) return point(p1.x+p2.x,p1.y+p2.y); point operator-(point p1,point p2) return point(p1.x-p2.x,p1.y-p2.y); 第8章 多态性 8.2.4 其它运算符重载 比较运算符重载(如,=,=,!=)。 赋值运算符重载(如=,+=,-=,*=,/=)。 下标运算符“”重载。 下标运算符“”通常用于取数组中的某个元素,通过下标运算 符重载,可以实现数组下标的越界检测等。 运算符new和delete重载。 通过重载new和delete,可以克服new和delete的不足,使其按要 求完成对内存的管理。 逗号运算符“,”重载。 逗号运算符构成的表达式为“左操作数,右操作数”,该表达式 返回右操作数的值。 第8章 多态性 8.2.5 赋值运算符“=”的重载 (例test_array_constructor.cpp) 对任一类X,如果没有用户自定义的赋值运算符函数,那么系统自动 地为其生成一个缺省的赋值运算符函数,定义为类X中的成员到成员 的赋值,例如: X 就调用缺省的赋值运算符函数,将对象obj2的数据成员的值逐 个赋给对象obj1的对应数据成员中。 第8章 多态性 1.指针悬挂问题 在某些特殊情况下,如类中有指针类型时,使用缺省的赋值运算符函数会产 生错误。 例 使用缺省的赋值运算符函数产生错误的例子 。 #include #include class string public: string(char *s) ptr=new charstrlen(s)+1; strcpy(ptr,s); string() delete ptr; void print() cout #include class string public: string(char *s) ptr=new charstrlen(s)+1; strcpy(ptr,s); string() delete ptr; void print() cout class base/定义基类base public: void who() coutwho(); ptr= /指向第一个子类 ptr-who(); ptr= /指向第二个子类 ptr-who(); obj1.who(); /分别指向子类 obj2.who(); 程序运行结果: This is the class of base! (a) This is the class of base! (b) This is the class of base! (c) This is the class of derive1! (d) This is the class of derive2! (e) 就是说,通过指针引起的普通 成员函数调用,仅仅与指针的类型 有关,而与指针正指向什么对象无 关。在这种情况下,必须采用显式 的方式调用派生类的函数成员。 第8章 多态性 要实现这种功能,就需要引入虚函数的概念。这里, 只需将基类的who()函数声明为虚函数即可。 8.3.2 虚函数的定义及使用 1.虚函数的定义 虚函数的定义是在基类中进行的。它是在基类中需要定 义为虚函数的成员函数的声明中冠以关键字virtual。当基 类中的某个成员函数被声明为虚函数后,此虚函数就可以 在一个或多个派生类中被重新定义,在派生类中重新定义 时,其函数原型,包括返回类型、函数名、参数个数、参 数类型以及参数的顺序都必须与基类中的原型完全相同。 第8章 多态性 一般虚函数的定义语法如下: virtual(形参表) 函数体 其中,被关键字virtual说明的函数为虚函数。特别要注意的是 ,虚函数的声明只能出现在类声明中的函数原型声明中,而不能出 现在成员的函数体实现的时候。 需要注意,动态联编只能通过成员函数来调用或者通过指针、 引用来访问虚函数。如果使用对象名的形式访问虚函数,则将采用 静态联编方式调用虚函数,而无需在运行过程中进行调用。下面是 通过指针访问虚函数的例题。 第8章 多态性 例8-5 虚函数的作用。/test_virtual.cpp #include class Base public: Base(int x,int y) a=x; b=y; virtual void show() /定义虚函数show() coutshow(); /调用基类Base的show()版本 pc= pc-show(); /调用派生类Derived的show()版本 程序运行结果如下: Base- 60 60 Derived- 30 第8章 多态性 2. 虚函数与重载的关系 在一个派生类中重新定义基类的虚函数是函数重载的另一种特 殊形式,但它不同于一般的函数重载。 一般的函数重载,只要函数名相同即可,函数的返回类型及所带 的参数可以不同。但当重载一个虚函数时,也就是说在派生类中重 新定义此虚函数时,要求函数名、返回类型、参数个数、参数类型 以及参数的顺序都与基类中的原型完全相同,不能有任何的不同。 3多继承中的虚函数 在多继承中由于派生类是由多个基类派生而来的,因此,虚函 数的使用就不像单继承那样简单。请看下面的例题。 第8章 多态性 【例8-6】多继承中使用虚函数例题。 #include class base1 public: virtual void who() /函数who()为虚函数 coutwho(); ptr2= ptr2-who(); ptr1= ptr1-who(); ptr2= ptr2-who(); 此时,程序执行的结果为 this is the class of base1! this is the class of base2! this is the class of derive! this is the class of base2! 从上面的例子看出,派生类 derive中的函数who()在不同的场 合呈现不同的性质。如相对base1 路径,由于在base1中的who()函 数前有关键字virtual,所以它是 一个虚函数;若相对于base2派生 路径,在base2中的who()函数为 一般函数,所以,此时它只是一 个重载函数。 第8章 多态性 若一个派生类,它的多个基类中有公共的基类,在公共基类中 定义一个虚函数,则多重派生以后仍可以重新定义虚函数,也就 是说,虚特性是可以传递的。请看下面的例题。 【例8-7】多继承中虚特性的传递例题。 #include class base public: virtual void who()/定义虚函数 coutwho(); ptr2 = ptr2-who(); 此时,程序执行的结果为 This is the class of derive! This is the class of derive! 第8章 多态性 假设基类和派生类都只有一个公有的数据成员, 其中类A有vfunc1和vfunc2两个虚函数和func1和func2 两个实函数。类A公有派生类B,类B改写vfunc1和 func1函数,它又作为类C的基类,公有派生类C。类 C也改写vfunc1和func1函数。 下图给出3个类建立的vptr和vtable之间的关系图 解以及实函数与虚函数的区别。 8.3.3 进一步探讨虚函数与实函数的区别 第8章 多态性 第8章 多态性 首先给vptr分配地址,它所占字节数决定对象中最长数据成员 的长度。因为3个类的数据成员都是整型,所以VC为vptr分配4个 字节。如果有double型的数据,则要分配8个字节。【例9.3】是演 示这一关系的程序。 从图中可见,对象的起始地址是vptr。它指向vtable,vtable为每 个虚函数建立一个指针函数,如果只是继承基类的虚函数,则它们 调用基类的虚函数,这就是b和c的vtable表中(*vfunc2)( )项所描述 的情况。如果派生类改写了基类的虚函数,则调用自己的虚函数, 这就是b和c的vtable表中(*vfunc1)( )项所描述的情况。 实函数不是通过地址调用,用带底纹的方框表示,它们由对象的名 字支配规律决定。 【例9-7】是程序实现。 第8章 多态性 【例8-7】实函数和虚函数调用过程。 #include using namespace std; class A public: int m_A; A(int a)m_A=a; void func1()coutvfunc1(); /调用A:vfunc1( ) pa-vfunc2(); /调用A:vfunc2( ) pa-func1(); /调用A:func1( ) pa-func2(); /调用A:vfunc2( ) coutvfunc1(); /调用B:vfunc1( ) pa-vfunc2(); /调用A:vfunc2( ) pa-func1(); /静态联编,只能调用A:func1( ) pa-func2(); /静态联编,只能调用A:func2( ) coutvfunc1(); /调用C:vfunc1( ) pa-vfunc2(); /调用A:vfunc2( ) pa-func1(); /静态联编,只能调用A:func1( ) pa-func2(); /静态联编,只能调用A:func1( ) coutvfunc1(); /调用B:vfunc1( ) pb-vfunc2(); /调用A:vfunc2( ) pb-func1(); /静态联编,调用B:func1() pb-func2(); /静态联编,只能调用A:func2() 第8章 多态性 coutvfunc1(); /调用C:vfunc1() pb-vfunc2(); /调用A:vfunc2() pb-func1(); /静态联编,只能调用B:func1() pb-func2(); /静态联编,只能调用A:func2() coutvfunc1(); /调用C:vfunc1( ) pc-vfunc2(); /调用A:vfunc2( ) pc-func1(); /静态联编,调用C:func1() pc-func2(); /静态联编,只能调用A:func2( ) 第8章 多态性 对象a有一个整型数据,应分配4个字节,vptr也是4个字节, 总共8个字节。对象b和c依次增加一个整型数据成员,内存分配 也顺增4个字节。输出结果如下: 8,12.16 0012FF78,0012FF7C /vptr, m_A 0012FF6C,0012FF70,0012FF74 /vptr, m_A, m_B 0012FF5C,0012FF60,0012FF64,0012FF68 /vptr, m_A, / m_B, m_C / A* pa= A:vfunc1() A:vfunc2() A:func1() 第8章 多态性 A:func2() / pa= B:vfunc1() A:vfunc2() A:func1() A:func2() /pa= C:vfunc1() A:vfunc2() A:func1() A:func2() / B* pb= B:vfunc1() A:vfunc2() B:func1() A:func2() / pb= C:vfunc1() A:vfunc2() B:func1() A:func2() /C* pc= C:vfunc1() A:vfunc2() C:func1() A:func2() 第8章 多态性 8.3.4 构造函数和析构函数调用虚函数 在构造函数和析构函数中调用虚函数时,采用静态联编,即它们所调 用的虚函数是自己的类或基类中定义的函数,但不是任何在派生类中 重定义的虚函数。下面给
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 2025年Z世代消费行为对新兴品牌成长路径影响报告
- 2024年演出经纪人之演出经纪实务考试题库带解析答案
- 广东绿道工程监理规划
- 一岗双责及新安法重点解读安全教育培训
- 第22课《礼记二则-大道之行也》课件
- 农产品质量安全追溯体系
- 2025届高考物理大一轮复习课件 第六章 第28课时 动能定理在多过程问题中的应用
- 化学●安徽卷丨2024年安徽省普通高中学业水平选择性考试化学试卷及答案
- 2025年全国注册会计师考试审计知识全真模拟试卷及答案(共三套)
- 消防初级试题及答案
- 农场转让合同协议书模板
- 2024-2025学年人教版数学一年级下学期期末模拟试卷(含答案)
- 2025-2030中国共享单车服务行业市场现状供需分析及投资评估规划分析研究报告
- 安徽省合肥一中2025届高三最后一卷英语试题及答案
- 有关工厂实习心得体会模版
- 2025年法律职业资格(客观题)重点考点大全
- 2025年组织行为学专业考试试题及答案
- 不寐的中医护理常规
- 2024年直播电商高质量发展报告
- 创新设计思维
- 客诉处理培训课件
评论
0/150
提交评论