版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
1、第八章 多态性,多态性作用 函数重载 运算符重载 虚函数 抽象类 拷贝构造函数 友元,8.1 程序的多态性,多态性:在程序中同一符号或名字在不同情况下具有不同解释 多态性的两种最基本形式 编译时多态性:指在程序编译阶段即可确定下来的多态性 由重载机制实现: 函数重载 运算符重载 运行时多态性:指必须等到程序动态运行时才可确定的多态性 由继承结合虚函数的动态绑定实现,8.2 函数重载,函数重载:同一作用域中允许多个函数使用同一函数名,但函数原型的形式参数的个数或者对应位置的类型不同 一、函数重载的方法 C语言不支持函数重载,所以每一个函数必须具有唯一的名字 例:求绝对值int abs(int);
2、整型 long labs(long);长整型 float fabs(float);浮点型 C+的函数重载:编译程序根据实际参数的个数与相应位置的类型选择调用哪一个版本的重载函数,例1:,#include int abs(int x) / 整数类型数据的绝对值函数 cout = 0 ? x : -x); double abs(double x) / 浮点类型数据的绝对值函数 cout = 0.0 ? x : -x); long abs(long x) / 长整数类型数据的绝对值函数 cout = 0 ? x : -x); int main() cout abs(-5) n;/ 调用abs()的整
3、数版本 cout abs(-5L) n;/ 调用abs()的长整数版本 cout abs(3.14) n;/ 调用abs()的浮点版本 return 0; ,二、函数重载的注意事项,编译程序选择相应的重载函数版本时函数返回值类型不起作用 例:int fun(int); double fun(int);不是重载函数 用typedef声明的类型别名,并没有创建新的数据类型 例:typedef double MONEY; double calculate(double income); MONEY calculate(MONEY income); 不是重载函数 不同参数传递方式无法区别重载函数 例:
4、void fun(int); void fun(int 不是重载函数,三、函数重载的二义性原因: 1. 函数调用时参数的隐式类型转换,函数调用时,编译程序选择重载函数的原则:如果函数调用的实际参数类型与一个重载函数的形式参数类型完全匹配,则选择调用该重载函数;如果找不到与实际参数类型完全匹配的函数原型,但如果将实参类型转换为更高级类型后能找到完全匹配,编译程序将选择该调用重载函数。 p234 例:int fun(int );fun(A); 隐式类型转换是由编译程序自动完成,易引起函数重载的二义性,例2:,#include float abs(float x) return (x = 0 ? x
5、 : -x); double abs(double x) return (x = 0 ? x : -x); int main() cout abs(3.14) n;/ 调用abs(double)的版本 / cout abs(-5) n;/ 错误,编译程序无法确定调用abs()的哪一个版本 return 0; ,2. 在重载函数中使用缺省参数也可能造成二义性的例3:,#include int func(int i) return i; int func(int i, int j = 10) return i * j; int main() cout func(3, 4) n;/ 调用func(i
6、nt, int) / cout func(5) n;/ 错误,编译程序无法确定调用func()的哪一个版本 return 0; ,例4:指出下面程序的输出结果。,#include ? void print(int x) coutCalling print(int) with x.n; void print(int x, int y) coutCalling print(int, int) with x, y .n; void print(double x) coutCalling print(double) with x.n; void main()运行结果: print(800); prin
7、t(3.14159); print(3, 4); float f=1.5; print(f); ,Calling print(int) with 800,Calling print(double) with 3.14159,Calling print(int, int) with 3, 4,Calling print(double) with 1.5,四、构造函数重载 为一个类提供了初始化对象的各种方法,#include class COMPLEX / 定义复数类COMPLEX的类界面 public: COMPLEX(double r = 0, double i = 0);/ 构造函数一 CO
8、MPLEX(const COMPLEX,COMPLEX:COMPLEX(double r, double i) real = r; image = i; COMPLEX:COMPLEX(const COMPLEX/返回对象本身 ,COMPLEX COMPLEX:subtract(const COMPLEX ,8.3 拷贝构造函数,拷贝构造函数:以类类型本身做形式参数,用一个已存在的该类对象初始化新创建的对象。 每一个类都必须有一个拷贝构造函数: 用户根据自己的需要显式定义 若用户未提供,则该类使用由系统提供的缺省的拷贝构造函数:使用逐位复制方式利用另一个对象来初始化新创建对象。,拷贝构造函数的
9、用途,在声明语句中用一个对象初始化另一个对象class X X a;/调用X的普通构造函数 X b = a ; /调用X的拷贝构造函数; X b(a); /用a对象初始化b 将一个对象作为实参,以按值调用方式传递给被调用函数的形参对象void fun(X temp) fun(a); /a传递给fun函数,创建 形参temp对象时,调用X的拷贝构 造函数用a对象初始化temp,temp 生存期结束时,调用析构函数释放 生成一个临时对象作为函数的返回结果 X fun2() b = fun2( ); X t; t - 临时对象 - b 拷贝构造函数 赋值运算符 return t ; 当函数返回一对象
10、时,系统将自动创建一个临时对象来保存函数的返回值。此临时对象在创建时调用拷贝构造函数,当函数调用表达式结束后,调用析构函数撤销。,拷贝构造函数的形式,以类类型本身做形式参数, 该参数传递方式为按引用调用:使形参为实参的别名,避免在函数调用过程中生成形参副本 该形参一般声明为const:因为按引用调用是双向的,为确保在拷贝构造函数中不修改实参的值,所以用const 例如:X:X(const X ,例:,#include class FOO public:FOO(int i) cout Constructing.n; pointer = new int; *pointer = i; FOO(con
11、st FOO,void display(FOO obj) cout obj.get() n; return; int main() FOO obj1(15); FOO obj2 = obj1;/ 调用拷贝构造函数 display(obj2); / 调用拷贝构造函数用实参obj2初始化形参obj return 0; 运行结果: Constructing. Copy constructing. Copy constructing. 15 Destructing. Destructing. Destructing.,例2,#include #include class NAME public:NAM
12、E() string = NULL; cout Constructing.n; NAME() cout Destructing.n; if (string != NULL) delete string; void show()cout string n; void set(char* s) if (string!=NULL)delete string; string = new charstrlen(s) + 1; if (string!=NULL)strcpy(string, s); private: char* string; ;,NAME get_name() NAMEtemp_obj;
13、/ chartemp_str250; / cout temp_str; temp_obj.set(temp_str); / return temp_obj; / / void main() NAME myname; myname = get_name(); myname.show(); ,运行结果: Constructing.myname的构造 Constructing.temp_obj的构造 Input your name: Lee Destructing.释放temp_obj,delete string Destructing.函数返回的临时对象被释放 Null pointer assig
14、nmentdelete出问题 (此行输出的字符串是不确定的!) Destructing.myname被释放 Null pointer assignmentdelete出问题,释放局部变量temp_obj:new分配的2000H空间被释放 3. 临时对象赋给myname, myname.string的值置为2000H 4. 撤销临时对象,动态分配的2000H空间已释放,再释放会出问题 5. myname.show()输出2000H上的内容,不确定 6. 撤销myname,delete出问题,修改:加入拷贝构造函数的定义,NAME:NAME(const NAME get_name函数返回, 创建临
15、时对象,调用拷 贝构造函数 temp_obj.string 临时对象.string,2000H,3000H,运行结果: Constructing. Constructing. Input your name: Lee Destructing. Destructing. Lee Destructing.,8.4 运算符重载一、运算符重载,定义:在不同情况下对同样的运算符做不同的解释 运算符函数 二元运算符是一个具有两个参数的函数 一元运算符是一个具有一个参数的函数 不能重载的运算符: : .*?:#sizeof,二、类成员运算符重载,一般形式: 类型 类名:operator (参数表) / 运算
16、符函数体 例:p243 程序8.4.1 class COMPLEX public:COMPLEX(double r = 0, double i = 0);/ 构造函数 COMPLEX(const COMPLEX,int main() COMPLEX c1(1, 2);/ 定义一个值为1 + 2i的复数c1 COMPLEX c2(2);/ 定义一个值为2的复数c2 COMPLEX c3(c1);/ 用COMPLEX(const COMPLEX ,注意:,当重载二元运算符,运算符成员函数只需显式传递一个参数,这是二元运算符的右操作数,而左操作数则是该类对象本身,用this指针隐式传递。 c1 =
17、c2 + c3 ; c1 = c2.operator +(c3); 当重载一元运算符,运算符成员函数没有参数,其使用隐式传递的this指针做参数。,三、重载赋值运算符=,类的赋值运算符=可以被重载,但重载的赋值运算符不能被继承。 若用户没有为一个类重载赋值运算符时,编译程序将生成一个缺省的赋值运算符,把源对象逐位地拷贝到目标对象 c1 = c2 ; 拷贝构造函数与赋值运算符的区别 拷贝构造函数要创建一个新对象 赋值运算符则是改变一个已存在的对象的值,四、例:,#include class STRING private:int length; char *Buffer; public:STRIN
18、G(); STRING(const char *str); STRING(); ; STRING:STRING() length = 0; Buffer = NULL; STRING:STRING(const char *str) length = strlen(str); Buffer = new charlength+1; if (Buffer!=NULL) strcpy(Buffer,str); STRING:STRING() if (Buffer) delete Buffer; ,void main() STRING aString(This); STRING bString(That
19、); aString = bString; aString: length Buffer bString: length Buffer 赋值后aString的数据成员Bufer原来指向的字符串所占用的存储块永久丢失,解决方法:重载赋值运算符 void STRING:operator=(const STRING ,五、友元,友元(friend)关系允许类的设计者选择出一组其他的类或函数,使得它们可以访问该类的私有和受保护成员。 在类的声明中,用friend声明的函数或类,即是该类的友元 一个类的友元可以是: 外部函数(不属于任何类的函数) 另一个类 其他类的成员函数 友元破坏了封装性,不可滥用,
20、例1:外部函数做友元,/ 类VALUE中定义了一个友元函数set(), / 注意set()不是该类的成员函数 class VALUE public: /声明set()为VALUE的友元 friend void set(VALUE obj, int x); private: int value; ; / 实现友元函数set() void set(VALUE obj, int x) obj.value = x;/ set()可以象VALUE成员函数一样访 / 问obj的私有成员 return; ,例2:另一个类做友元,class Y;/ Y类的引用性声明语句 class X public:frie
21、nd Y;/ 把Y类声明为X类的友元,则Y类 /的所有成员函数都是X的友元 private:int k ; void m_Xfunc( ); ; class Y public: void m_Yfunc( X / Y类的成员函数是X的友元,可以访问 / X的私友和受保护的成员 ,例3:其他类的成员函数做友元,class Y public: void Yfunc( ); ; class X public:friend void Y:Yfunc( ); / 把Y类的Yfunc函数声明为X类的友元 private:int k ; void m_Xfunc( ); ; void Y:Yfunc( X
22、/ 该函数是X的友元,可以访问 / X的私友和受保护的成员 ,六、友元运算符重载 1. 类成员函数运算符重载引起的问题,例: y+30; 合法调用y.operator+(30) 30+y; 则不合法,因为左操作数不是该类的对象,因此无法调用该类重载的运算符 类成员函数运算符重载函数在使用时,二元运算符的左操作数必须为该类对象,否则出错,2. 友元运算符重载,参数规则: 一元运算符必须显式声明一个参数 二元运算符必须显式声明二个参数 不能做友元重载的运算符 =( ) - 友元函数不是该类的成员,因此在友元函数中不能使用this指针,例:,#include class INTEGER public
23、: / 构造函数 INTEGER(int i = 0) value = i; return; / 拷贝构造函数 INTEGER(const INTEGER ,/ 用友元函数重载加法运算符 friend INTEGER operator +(INTEGER left, INTEGER right) INTEGER temp; temp.value = left.value + right.value; return temp; private:int value;/ 私有数据 ; void main() INTEGER x(10);/ 定义整数对象,用构造函数来初始化 INTEGER y = x
24、;/ 定义整数对象,用拷贝构造函数来初始化 INTEGER z;/ 定义整数对象,用构造函数的缺省参数初始化 y = x + 2;/ 合法调用 z = 30 + y;/ 也是合法的调用 ,七、运算符重载的其他规则,重载运算符无法改变任何运算符的优先级与结合性质; 重载运算符不可改变运算符的操作数个数; 重载运算符不可使用缺省参数; 除了赋值运算外,重载运算符可由派生类继承下去; 运算符=、()、和-可作为类成员运算符,但不可作为友元运算符。,8.5 虚函数 一、绑定,绑定:将一个函数调用链接上相应的函数体代码的过程 绑定的两种形式: 静态绑定(早期绑定):在编译阶段绑定 缺省的函数调用方式 动
25、态绑定(晚期绑定):在程序运行阶段绑定,二、虚函数,虚函数是类中被冠以virtual的成员函数 虚函数在公有继承结构中在一个或多个派生类中被重定义,在被调用过程中,通过指向基类的指针实现动态绑定,例1:,#include class BASE public: void who( ) coutwho(); p= / 基类指针可指向派生类对象 ,输出结果:BASE BASE不管p指向什么对象,通过p三次调 BASE用的都是基类的who函数 原因:普通成员函数的调用采用的是静态绑定 通过指针引起的普通成员函数调用,仅仅与指针(或引用)的基类型有关,而与此刻该指针正在指向什么对象无关,例2:希望p-w
26、ho();依赖于运行时p所指向的对象,则基类修改为:,class BASE public: virtual void who( ) cout“BASEn”; ; 把基类中的who函数定义成虚函数,实现动态绑定 输出结果:BASE First Derivation Second Derivation 基类的虚函数定义了一种接口,在派生类中为此接口定义不同的实现版本,由于虚函数的解释机制,实现“单界面、多实现版本”的思想,在运行时刻才将函数与不同实现版本进行匹配 运行时的多态性,虚函数的说明,用虚函数实现动态绑定的关键:必须用指向基类的指针来访问虚函数 若一函数是类中的虚函数,则称该函数具有虚特性 在派生类中重定义从基类中继承过来的函数:函数原型保持不变 仅仅返回类型不同,其余相同,错误 函数原型不同,仅函数名相同,这是一般的函数重载,虚特性丢失 当一个派生类没有重新定义虚函数时,则使用其基类定义的虚函数版本,例3:,#include class BASE public: virtual void f1( ) virtual void f2( ) v
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 2025-2030中国塑代木市场应用状况与供应情况预测报告
- 幼年粒单核细胞白血病总结2026
- 大班综合有趣的年俗
- 就业指导课程体系建设
- 社区防汛应急处置
- 口腔运营渠道策划方案范文相关7篇
- 服装人职业规划:从设计到管理
- 2026年国家公务员行测真题卷
- 2025年广西壮族自治区南宁市初二学业水平地生会考考试题库(附含答案)
- 2025年广西壮族自治区防城港市八年级地生会考试题题库(答案+解析)
- ESG基础知识培训课件
- 法律效应的婚内保证书
- 育肥猪场月度汇报
- 多重耐药感染临床案例深度剖析
- 北京大学2022年强基计划笔试数学试题(解析版)
- 2024-2025学年清华大学版(2024)A版初中信息科技八年级下册(全册)知识点复习要点归纳
- 五年级下册数学期中必考易错题应用题六大类
- 密闭式静脉输血操作流程
- 审计案例第2章审计风险评估案例
- 2025年中国菠菜种植行业市场全景评估及发展战略规划报告
- 中国食物成分表标准版第6版
评论
0/150
提交评论