09C++第12讲多态性
c++第12章
ps=&s; ps->Calculate(); cout<<"本科生:"<<'\t'; ps->Print(); ps=&g; ps->Calculate(); cout<<"研究生:"<<'\t'; ps->Print(); return 0;} 第二行,它屏蔽了 结果为: 基类的同名函数。 本科生:物理 80学时 5学分 研究生:物理 80学时 4学分 本科生:数学 160学时 10学分 研究生:数学 160学时 8学分
第12章 多态性与虚函数
12.1 12.2 12.3 12.4 多态性的概念 一个典型的例子 虚函数 纯虚函数与抽象类
12.1 多态性的概念 本章所讲的多态性指在继承类中与基类同名、 同参数、同类型函数的不同行为。 多态就是指不同的对象接受到相同的消息时产 生不同的响应动作,即对应相同的函数名,却执行 了不同的函数体。 多态性分为两类: 静态多态性和动态多态性。 以前学过的函数重载和运算符重载实现的多态性属 于静态多态性,在程序编译时系统就能决定调用的 是哪个函数,因此静态多态性又称编译时的多态性。 静态多态性是通过函数的重载实现的。
是指向基类的指针,可实际上用了 派生类中的Calculate()。如果不是 虚函数,第四行输出是10学分。
二、静态关联与动态关联 关联——确定调用的具体对象的过程。在这里 是指把一个函数名与一个类对象捆绑在一起,建立 关联。一般地说,关联指把一个标识符和一个存储 地址联系起来。 静态关联:在编译时即可确定其调用的虚 函数属于哪一个类,这种关联在编译阶 支持 段完成的。又称为早期关联。静态关联 两种 能够实现编译时多态。 关联 方式 动态关联:是在运行阶段把虚函数和类对 象“绑定”在一起的。这种关联要在程 序运行时动态进行,所以又称为滞后关 联。动态关联可以实现运行时多态。
C_多态
C#的多态一、什么是多态可以把一组对象放到一个数组中,然后调用它们的方法,在这种场合下,多态性作用就体现出来了,这些对象不必是相同类型的对象。
当然,如果它们都继承自某个类,你可以把这些派生类,都放到一个数组中。
如果这些对象都有同名方法,就可以调用每个对象的同名方法。
同一操作作用于不同的对象,可以有不同的解释,产生不同的执行结果,这就是多态性。
多态性通过派生类重载基类中的虚函数型方法来实现。
在面向对象的系统中,多态性是一个非常重要的概念,它允许客户对一个对象进行操作,由对象来完成一系列的动作,具体实现哪个动作、如何实现由系统负责解释。
“多态性”一词最早用于生物学,指同一种族的生物体具有相同的特性。
在C#中,多态性的定义是:同一操作作用于不同的类的实例,不同的类将进行不同的解释,最后产生不同的执行结果。
C#支持两种类型的多态性:● 编译时的多态性编译时的多态性是通过重载来实现的。
对于非虚的成员来说,系统在编译时,根据传递的参数、返回的类型等信息决定实现何种操作。
● 运行时的多态性运行时的多态性就是指直到系统运行时,才根据实际情况决定实现何种操作。
C#中,运行时的多态性通过虚成员实现。
编译时的多态性为我们提供了运行速度快的特点,而运行时的多态性则带来了高度灵活和抽象的特点。
二、实现多态多态性是类为方法(这些方法以相同的名称调用)提供不同实现方式的能力。
多态性允许对类的某个方法进行调用而无需考虑该方法所提供的特定实现。
例如,可能有名为Road 的类,它调用另一个类的Drive 方法。
这另一个类Car 可能是SportsCar 或SmallCar,但二者都提供Drive 方法。
虽然Drive 方法的实现因类的不同而异,但Road 类仍可以调用它,并且它提供的结果可由Road 类使用和解释。
可以用不同的方式实现组件中的多态性:● 接口多态性。
● 继承多态性。
● 通过抽象类实现的多态性。
接口多态性多个类可实现相同的“接口”,而单个类可以实现一个或多个接口。
多态性
多态性。
1.多态性的概念a)多态性是面向对象程序设计的重要特征之一。
b)多态性是指发出同样的消息被不同类型的对象接收时导致完全不同的行为。
c)消息——主要指对类的成员函数的调用。
d)多态的实现:i.函数重载静态:编译时的多态ii.运算符重载iii.虚函数----动态:运行时的多态2.联编(binding):是指计算机程序自身彼此关联的过程。
3.联编工作在编译连接阶段完成的情况称为:静态联编4.联编在程序运行阶段完成的情况称为:动态联编。
5.问题举例——复数的运算a)用“+”、“-”能够实现复数的加减运算吗?b)实现复数加减运算的方法——重载“+”、“-”运算符6.运算符重载的实质a)运算符重载是对已有的运算符赋予多重含义b)必要性i.C++中预定义的运算符其运算对象只能是基本数据类型,而不适用于用户自定义类型(如类)c)实现机制i.将指定的运算表达式转化为对运算符函数的调用,运算对象转化为运算符函数的实参。
ii.编译系统对重载运算符的选择,遵循函数重载的选择原则。
7.规则和限制a)可以重载C++中除下列运算符外的所有运算符:. .* :: ?:b)只能重载C++语言中已有的运算符,不可臆造新的。
c)不改变原运算符的优先级和结合性。
d)不能改变操作数个数。
e)经重载的运算符,其操作数中至少应该有一个是自定义类型。
8.两种形式a)重载为类成员函数。
b)重载为友元函数。
9.运算符函数a)声明形式i.函数类型operator 运算符(形参){......}b)重载为类成员函数时i.参数个数=原操作数个数-1 (后置++、--除外)ii.c)重载为友元函数时参数个数=原操作数个数,且至少应该有一个自定义类型的形参。
10.运算符成员函数的设计a)双目运算符Bi.如果要重载 B 为类成员函数,使之能够实现表达式oprd1 B oprd2,其中oprd1 为A 类对象,则B 应被重载为 A 类的成员函数,形参类型应该是oprd2 所属的类型。
多态性与虚函数.doc
多态性与虚函数多态性的概念多态性(polymorphism)是面向对象程序设计的一个重要特征。
利用多态性可以设计和实现一个易于扩展的系统。
有过非面向对彖语言开发经历的人,通常对这一章节的内容会觉得不习惯,因为很多人错误地认为,支持类的封装的语言就是支持血向对象的,其实不然,Visual BASIC 6.0是典型的非面向对象的开发语言,但是它的确是支持类,支持类并不能说明就是支持面向对象,能够解决多态问题的语言,才是真正支持面向对象的开发的语言,所以务必提醒有过其它非面向对象语言基础的读者注意!多态的意思是指具有不同功能的函数可以用同一个函数名,这样就可以用一个函数名调用不同内容的函数。
其实,我们己经接触过多态性的现象,例如函数的重载、运算符重载都是多态现象。
只是那时没有用到多态性这一专业术语而已。
例如,使用运算符”+”使两个数值相加,就是发送一个消息,它要调用operator+函数。
实际上,整型、单精度型、双精度型的加法操作过程是互不相同的,是由不同内容的函数实现的。
显然,它们以不同的行为或方法來响应同一消息。
在面向对象方法中一般是这样表述多态性的:向不同的对象发送同一个消息,不同的对象在接收时会产生不同的行为(即方法)。
也就是说,每个对象可以用自己的方式去响应共同的消息。
从系统实现的角度看,多态性分为两类:静态多态性和动态多态性。
以前学过的函数重载和运算符重载实现的多态性属于静态多态性,在程序编译时系统就能决定调用的是哪个函数,因此静态多态性乂称编译时的多态性。
静态多态性是通过函数的重载实现的(运算符重载实质上也是函数重载)。
动态多态性是在程序运行过稈中才动态地确泄操作所针对的对彖。
它又称运行时的多态性。
动态多态性是通过虎函数(virtual function)实现的。
本章中主要介绍动态多态性和虚函数。
要研究的问题是:当一个基类被继承为不同的派生类时,各派生类可以使用与基类成员相同的成员名,如果在运行时用同一个成员名调用类对象的成员,会调用哪个对象的成员?也就是说,通过继承而产生了相关的不同的派生类,与基类成员同名的成员在不同的派生类中有不同的含义。
多态性
多态性一、什么是多态性 1、多态性的定义术语“多态性”可以理解为“有许多形式”,一个“多态性引用”是可以在不同的时间指向不同类型的引用变量。
利用多态性调用的方法能够由一个调用改变为另一个调用。
分析下面的代码:obj.doIt(); 若obj引用是多态性的,它就可以在不同时刻指向不同类型的对象。
若将这行代码写在循环中或者写在一个调用多次的方法中,其结果是每次执行时可调用不同版本的doIt方法。
现实中,关于多态的例子不胜枚举,比方说按下 F1 键这个动作,如果当前在 Flash 界面下弹出的就是 AS 3 的帮助文档;如果当前在 Word 下弹出的就是 Word 帮助;在 Windows 下弹出的就是 Windows 帮助和支持。
同一个事件发生在不同的对象上会产生不同的结果。
汽车的方向盘就是一个简单的多态性示例。
无论实际的方向控制机制是什么类型的,方向盘(也就是接口)都是一样的。
也就是说,无论你的汽车是手动操纵、电力操纵还是齿轮操纵,方向盘使用起来都是一样的。
因此,只要你知道如何操作方向盘,那么就可以驾驶任何类型的汽车。
2、多态的好处:(1)可替换性(substitutability)。
多态对已存在代码具有可替换性。
例如,多态对圆Circle类工作,对其他任何圆形几何体,如圆环,也同样工作。
(2)可扩充性(extensibility)。
多态对代码具有可扩充性。
增加新的子类不影响已存在类的多态性、继承性,以及其他特性的运行和操作。
实际上新加子类更容易获得多态功能。
例如,在实现了圆锥、半圆锥以及半球体的多态基础上,很容易增添球体类的多态性。
(3)接口性(interface-ability)。
多态是超类通过方法签名,向子类提供了一个共同接口,由子类来完善或者覆盖它而实现的。
(4).灵活性(flexibility)。
它在应用中体现了灵活多样的操作,提高了使用效率。
wk_ad_begin({pid : 21});wk_ad_after(21, function(){$('.ad-hidden').hide();}, function(){$('.ad-hidden').show();});(5)简化性(simplicity)。
13多态性
13.3 动态联编
1
13.3 动态联编
► 当调用虚函数时,先通过vptr找到虚函数表,然后再找出虚函数的 真正地址,再调用它。
► 派生类能继承基类的虚函数表,而且只要是和基类同名(参数也相 同)的成员函数,无论是否使用virtual声明,它们都自动成为虚函 数。如果派生类没有改写继承基类的虚函数,则函数指针调用基类 的虚函数。如果派生类改写了基类的虚函数,编译器将重新为派生 类的虚函数建立地址,函数指针会调用改写以后的虚函数。
4
13.1 多态性的概念
► (1)重载多态 ► 重载是多态性的最简单形式,分为函数重载和运算符重载。 ► 重定义已有的函数称为函数重载。在C++中既允许重载一般函数,
也允许重载类的成员函数。如对构造函数进行重载定义,可使程序 有几种不同的途径对类对象进行初始化。 ► C++允许为类重定义已有运算符的语义,使系统预定义的运算符可 操作于类对象。如流插入(<<)运算符和流提取(>>)运算符 (原先语义是位移运算)。
13.1 多态性的概念
► 多态是指同样的消息被不同类型的对象接收时导致不同的行为。所 谓消息是指对类成员函数的调用,不同的行为是指不同的实现,也 就是调用了不同的函数。
► 从广义上说,多态性是指一段程序能够处理多种类型对象的能力。 ► 在C++中,这种多态性可以通过重载多态(函数和运算符重载)、
强制多态(类型强制转换)、类型参数化多态(模板)、包含多态 (继承及虚函数)四种形式来实现。
19
cout<<"Circle area="<<c.area()<<endl; //派生类对
C第章多态性精品PPT课件
cout<<“area of a:”<<a.area()<<endl;
cout<<“area of c:”<<c.area()<<endl;
Point *p=&c;
cout<<“area of c:”<<p->area()<<endl;
}
第05章 多态性
信工计算机教研室
覆盖技术
☺a.area()表达式明确告诉编译器,它调用的 是Point的成员函数area,输出0。
☺c.area()表达式明确表示调用的是Circle的 成员函数area,输出19.6349。
☺因为指针p的数据类型是Point,根据赋值 兼容性规则,p->area(),调用的是Point 的成员函数,输出0。
第05章 多态性
信工计算机教研室
4、访问被覆盖的方法
class CPoint {
int x,y; public:
double area() { return 0;} };
第05章 多态性
信工计算机教研室
覆盖技术
class Circle: public Point {
double radius; public:
Circle(double a,double b,double r) : Point(a, b) { radius=r; }
{ num=int(a+0.5); } void show() { cout<<num<<endl; } };
第05章 多态性
信工计算机教研室
成员函数重载
void main() {
多态性
p1->Speak();
p2=new Cat;
//动态生成Cat类对象
p2->Speak();
p3=new Dog;
//动态生成Dog类对象
p3->Speak();
Pet
&p4 = cat1;
//以Cat类对象初始化Pet类引用
p4 .Speak();
return 0;
}
运行结果
虚函数
定义:
在某基类中声明为virtual并在一个或多个派生类中被重 新定义的成员函数,称为虚函数。
int
max ( int x, int y ) { return x>y?x:y; }
float max ( float x, float y ) {return x>y?x:y; }
double max (double x, double y ) {return x>y?x:y; }
void main()
//派生类
{public: void Speak() { cout<<"miao!miao!"<<endl; }
};
class Dog : public Pet
//派生类
{public: void Speak() { cout<<"wang!wang!"<<endl; }
};
int main()
{
Pet
};
class cat : public pet
{
public:
void speak() { cout<< "miao!miao!"<<endl; }
第12章 多态性_1
例:定义一个计数器CC,对其重载运算符“+”
#include “iostream.h” class cc { int n; public: cc() { n=0;} cc( int k) { n=k;} cc operator+(cc a) { cc t; t.n=n+a.n; return t; } void disp( ){ cout<<n<<endl;} }; void main( ) { cc a1(5),a2(10),a; a=a1+a2; }
一、双目运算符成员函数 • 定义格式 类A operator 运算符(类C 形参) { ...... } 含义:运算符被重载为 A 类的成员函数。有 一个参数是隐含的,运算符函数是用this指 针隐式地访问类对象的成员。
运算符成员函数的设计
• 调用双目运算符 B的成员函数格式:
op1 B op2
例10.2 重载运算符“+”,使之能用于两个复数相加。 #include <iostream> using namespace std; class Complex {public: Complex( ){real=0;imag=0;} Complex(double r,double i){real=r;imag=i;} Complex operator+(Complex &c2); void display( ); 重载函数是Complex类 private: 中的成员函数+,有一个参 double real; 数是隐含的,运算符函数是 double imag; 用this指针隐式地访问类对 象的成员。 };
Time Time∷operator++(int) {Time temp(*this); sec++; if(sec>=60) { sec-=60; ++minute; } return temp; } int main( ) { Time t1(34,59),t2; cout<<″ time1 : ″; t1.display( ); ++t1; cout<<″++time1: ″; t1.display( ); t2=t1++; //将自加前的对象的值赋给t2 cout<<″time1++: ″; t1.display( ); cout<<″ time2 :″; t2.display( ); }
最新C 程序设计 第12章多态性与虚函数课件ppt
C++规定,当一个成员函数被声明为虚函数后,其 派生类中的同名函数都自动成为虚函数。因此在派 生类重新声明该虚函数时,可以加virtual,也可以 不加,但习惯上一般在每一层声明该函数时都加 virtual,使程序更加清晰。
如果在派生类中没有对基类的虚函数重新定义,则 派生类简单地继承其直接基类的虚函数。
class Student { protected: string name; public: Student(string n):name(n) {} virtual void PrintInfo(); };
void Student::PrintInfo() { cout<<"name: "<<name<<endl; }
class Graduate:public Student { protected: string advisor; public: Graduate(string n,string a):Student(n),advisor(a) {} void PrintInfo(); };
void Graduate::PrintInfo() { cout<<"name: "<<name<<" "<<"advisor: "<<advisor<<endl; }
即希望用基类指针来引用不同的对象(基类的或派 生类的),并能根据引用的对象不同,调用相应的 成员函数(表现出不同的行为)——“多态”。
显然,这里我们未能如愿。
12.3 虚函数
12.3.1 虚函数的作用
C++中的虚函数就是用来解决上节的问题的。 虚函数的作用是允许在派生类中重新定义与 基类同名的函数,并且可以通过基类指针或 引用来访问基类和派生类中的同名函数。
多态性
位点多态性是由于等位基因之间在特定的位点上DNA序列存在差异,也就是基因组中散在的碱基的不同,包 括点突变(转换和颠换),单个碱基的置换、缺失和插入。突变是基因多态性的一种特殊形式,单个碱基的置换 又称为单核苷酸多态性(single nucleotide polymorphism, SNP), SNP通常是一种二等位基因(biallelic) 或二态的变异。据估计,单碱基变异的频率在1/1000-2/1000。SNP在基因组中数量巨大,分布频密,检测易于自 动化和批量化,被认为是新一代的遗传标记。
类型
遗传和变异这一对既对立又统一的内在矛盾,在外在环境的影响下相互作用,促生了生物群体遗传多态性的 存在,进而提供了物种进Байду номын сангаас的动力。根据一个群体中各种变异类型的比例,可以把遗传多态性分为两种类型:
平衡型:一个群体中各种变异类型的比数可以长期保持不变,呈现所谓平衡型(或稳定)多态现象;
过渡型:一个群体中各种变异在一种类型取代另一种类型的过程中所呈现的多态现象,这里各种变异类型的 比数逐渐发生变化,因此称为过渡型(不稳定)多态现象。
基因
1
定义
2
成因
3
生物学作用
4
医学意义
5
检测方法
定义
基因多态性(gene polymorphism)是指处于随机婚配的群体中,同一基因位点可存在2种以上的基因型。 在人群中,个体间基因的核苷酸序列存在着差异性称为DNA基因多态性(gene polymorphism)。这种多态性可以 分为两类,即DNA位点多态性(site polymorphism)和长度多态性 (length polymorphism)。
度量
遗传多态性现象是指同一生物群体中,两种或两种以上变异类型或基因型并存的现象。一般认为每种变异型 的频率超过1%即可定为多态现象,不足1%的称为罕见变异型。
多态性——精选推荐
多态性回顾区分重载与覆盖?1.成员函数被重载的特征:(1)相同的范围(在同⼀个类中);(2)函数名字相同;(3)参数不同;(4)virtual 关键字可有可⽆。
2.覆盖是指派⽣类函数覆盖基类函数,特征是:(1)不同的范围(分别位于派⽣类与基类);(2)函数名字相同;(3)参数相同;(4)基类函数必须有virtual 关键字。
多态性:多态性是⾯向对象程序设计的重要特征之⼀。
简单的说,多态性是指⽤⼀个名字定义不同的函数,调⽤同⼀个名字的函数,却执⾏不同的操作,从⽽实现传说中的”⼀个接⼝,多种⽅法”!多态是如何实现绑定的?1. 编译时的多态性:通过重载实现2. 运⾏时的多态性:通过虚函数实现编译时的多态性特点是运⾏速度快,运⾏时的特点是⾼度灵活和抽象。
析构函数解析虚⽅法那节告诉⼤家析构器都是虚⽅法,有没有表⽰不解啊?从⼀个实例说起:ClsBase.cpp#include <iostream>class ClxBase{public:ClxBase(){};virtual ~ClxBase(){};virtual void doSomething(){std::cout << "Do something in class ClxBase!\n";}};class ClxDerived : public ClxBase{public:ClxDerived(){};~ClxDerived(){std::cout << "Output from the destructor of class ClxDerived!\n";};void doSomething(){std::cout << "Do something in class ClxDerived!\n";};};int main(){ClxBase *pTest = new ClxDerived;pTest -> doSomething();delete pTest;return 0;} 结果:Do something in class ClxDerived!Output from the destructor of class ClxDerived!请按任意键继续. . .栗⼦很简单,如果我们把类 ClxBase 析构函数前的 virtual 去掉,那输出结果就应该是: Do something in class ClsDerived!也就是说类 ClxDerived 的析构函数压根⼉没有被调⽤到!#include <iostream>class ClxBase{public:ClxBase(){};~ClxBase(){};virtual void doSomething(){std::cout << "Do something in class ClxBase!\n";}};class ClxDerived : public ClxBase{public:ClxDerived(){};~ClxDerived()//去掉了virtual{std::cout << "Output from the destructor of class ClxDerived!\n";};void doSomething(){std::cout << "Do something in class ClxDerived!\n";};};int main(){ClxBase *pTest = new ClxDerived;pTest -> doSomething();delete pTest;return 0;} 结果:Do something in class ClxDerived!请按任意键继续. . .⼀般下类的析构函数⾥⾯都是释放内存资源,⽽析构函数不被调⽤的话旧会造成内存泄露。
C语言中的多态性.ppt
point point::operator+(point q)
{ point p; p.x=x+q.x; p.y=y+q.y;
return p;
} 这两种方法的执行效率是完全不同的。后者的执行过程是这样的:创建一个局 部对象p(这时会调用构造函数),执行return语句时,会调用拷贝构造函数,将p的 值拷贝到主调函数中的一个无名临时对象中。当函数operator+ 结束时,会调用析 构函数析构对象p,然后p消亡。两种方法相比,前一种方法的效率高,因为它是 直接将一个无名临时对象创建到主调函数中
在调用后缀方式的函数时,参数int一般被传递给值0。(例 test_operator.cpp)
第8章 多态性 【例8-1】双目运算符重载为成员函数例题。//test_operator.cpp
#include<iostream.h> class point 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<<p3.get_x()<<“ "<<p3.get_y()<<endl; cout<<p4.get_x()<<“ "<<p4.get_y()<<endl; }
多态性类型
多态性类型多态类型一个群体中各种变异类型的比数可以长期保持不变,呈现所谓平衡型(或稳定)多态现象;也可以是一种类型在取代另一种类型的过程中所呈现的多态现象,这里各种变异类型的比数逐渐发生变化,因此称为过渡型(不稳定)多态现象。
平衡型多态人类的ABO血型由一个座位上3个复等位基因所控制(见)。
在各个人群中这3个等位基因的频率常不相同,可是它们之间的比例却长期保持不变。
例如等位基因B 在欧亚大陆交界处的人群中占16%以上,在英国约占4~6%,在这中间呈现一个梯度,这些百分数长期保持不变。
基因A的情况也相似,世界各地的多数人群中基因A约占0~10%,少数人群高于35%。
在拟暗果蝇的同一唾腺染色体上可以发现各种不同的倒位,在同一群体中这些倒位染色体以不同的频率并存。
虽然各种倒位的相对频率随着季节的不同而有规律地变动,可是在同一季节中各种倒位的频率保持稳定,而且各个不同的群体都有特定的频率。
另外一种普遍存在的多态现象反映在功能相同的酶的电泳图谱上。
在分析过的几乎任何一种生物中都发现有这类同工酶的存在,而且各个群体也常有它的多态特征。
在6种生物中测得群体中平均每一个体中有6.7%到12.3%的座位上的基因呈杂合状态,不同的等位基因编码不同的同工酶蛋白,人的杂合座位占6.7%。
过渡型多态椒花蛾有浅色的和深色的两种类型,这是一对决定的性状,深色是显性。
19世纪中叶在英国曼彻斯特所采集到的椒花蛾中不到1%是深色的,在1898 年则增加到99%。
在过去的120年中北欧和北美洲的许多浅色的蛾都逐渐为深色类型所取代。
在深色类型完全取代浅色类型以前,这两种类型构成不稳定的或过渡性的多态群体。
遗传机制自然选择是造成遗传多态的主要原因。
杂合体的选择优势在杂合体具有选择优势的情况下遗传多态性得以保持。
人类的镰形细胞贫血基因H和野生型基因H是共显性的。
正常纯合体H/H不患贫血症,可是对于疟疾的抵抗力较弱;镰形细胞纯合体(H/H)对于疟疾有较强的抵抗力,可是患贫血症,常在幼年死亡。
多态性——精选推荐
多态性
多态性:多态性是指允许不同⼦类型的对象对同⼀消息作出不同的响应。
简单的说就是⽤同样的对象引⽤调⽤同样的⽅法但是做了不同的事情。
多态性分为编译时的多态性和运⾏时的多态性。
如果将对象的⽅法视为对象向外界提供的服务,那么运⾏时的多态性可以解释为:当A 系统访问B系统提供的服务时,B系统有多种提供服务的⽅式,但⼀切对A系统来说都是透明的(就像电动剃须⼑是A系统,它的供电系统是B系统,B系统可以使⽤电池供电或者⽤交流电,甚⾄还有可能是太阳能,A系统只会通过B类对象调⽤供电的⽅法,但并不知道供电系统的底层实现是什么,究竟通过何种⽅式获得了动⼒)。
⽅法重载(overload)实现的是编译时的多态性(也称为前绑定),⽽⽅法重写(override)实现的是运⾏时的多态性(也称为后绑定)。
运⾏时的多态是⾯向对象最精髓的东西,要实现多态需要做两件事:1). ⽅法重写(⼦类继承⽗类并重写⽗类中已有的或抽象的⽅法);2). 对象造型(⽤⽗类型引⽤引⽤⼦类型对象,这样同样的引⽤调⽤同样的⽅法就会根据⼦类对象的不同⽽表现出不同的⾏为)。
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
double max(double x,double y) { return x>y?x:y; } }
编译时多态性 - 类中的成员函数重载举例
main() class Person { { private: Person p1; char Name[20]; Person p2(“Yang”,20,’m’); int Age; p1.ShowMe(); char Sex; p2.ShowMe(); public: return 0; } Person(); //构造函数 Person( *name, int age, char sex); //构造函数 ~Person(); //析构函数 void ShowMe();
不能定义虚构造函数,可以定义虚析构函数
例12-2 虚函数实现运行多态性
class Circle { protected: double radius; public: Circle(double r=0) { radius=r; } virtual double Area() { return 3.14*radius*radius; } };
二. 派生类对象替换基类对象
公有派生类对象替换基类对象
由于公有派生类全盘继承了基类的成员和访问权限,因 此,在C++中使用公有派生类对象替换基类的对象,实现本 来由基类对象完成的工作。
对象替换常见的形式
派生类对象给基类对象赋值 派生类对象可以初始化基类对象的引用 派生类对象的地址传递给基类的指针变量
静态联编技术 动态联编技术
联编-将程序中的函数调用与实现调用功能的程序代码关 联起来。
多态性的两种处理技术
静态联编技术-编译时多态性
在编译中确定调用哪一个同名函数。通过函数重 载和运算符重载实现。
动态联编技术-运行时多态性
在程序执行过程中确定调用哪一个同名函数。通 过继承和虚函数实现。
};
编译时多态性–派生类对基类成员函数的重载与覆盖
重载:成员函数的参数有区别,即个数或类型不同。 覆盖:成员函数参数完全相同,这时派生类将覆盖基 类的同名成员函数。即 派生类对象.成员函数() - 访问派生类中覆盖基类的 同名函数。 派生类对象.基类名::成员函数() - 可以访问基类中的 同名函数。
计算机程序设计(C++)
2007-2008第二学期
第12章 多态性
E-mail:
教师:杨振平 zpyang@
授 课 内 容
多态性概述 派生类对象替换基类对象 虚函数 抽象类 运算符重载
一. 多态性概述
多态性含义
在面向对象系统中, 多态性是指不同对象接收到相同的 消息时会产生不同的行为。 在C++程序中,“同名函数”即为多态性的表现形式。 C++语言具有处理“多态性”的方法技术。
例12-3
抽象类与具体类的接口和实现
从Point、Circle类中抽象出基类Shape。
class Shape
{ public: // 纯虚函数,在派生类中重载 virtual double Area() =0; //几何图形面积 virtual void PrintShapeName() const = 0; //输出名称 virtual void Print() const = 0; };
{
函数体
}
用途: 实现运行时多态性,即通过基类指针指向公有派生类 对象,可以访问派生类中同名覆盖的成员函数。
虚函数的使用限制
虚函数重定义时,必须保证函数名、返回值类型、 参数个数、参数类型等应于基类函数一致。 通过指针或引用调用虚函数,才能保证运行时多 态性的成立。
派生类中重定义的基类虚函数仍为虚函数,同时 可以省略virtual关键字。
如何使用PC->Area()访问派生类中覆盖基类的Area()成员函数?只需 将基类中的Area()函数定义为虚函数。
虚函数的作用:
从本质上讲,虚函数实现“一个接口,多种方法”的理念,即基 类内部的虚函数定义该函数的接口形式,而在派生类中对虚函数的定义格式
virtual 返回值类型 函数名(参数表)
编译时多态性 – 普通函数重载举例
找两个数中的大数
//整型 //测试用函数
int main()
{ int ai,bi;
int max(int x,int y) { return x>y?x:y; }
//双精度型
double ad,bd;
cin>>ai>>bi; cout<<max(ai,bi)<<endl; cin>>ad>>bd; cout<<max(ad,bd)<<endl return 0;
虚函数定义
虚函数定义
运行结果
四. 抽象类
纯虚函数定义格式: virtual 返回类型 函数名(参数表)=0
具体实现只能在派生类中完成。
;
抽象类
至少包含一个纯虚函数的类。
说明:
在基类中只作纯虚函数的声明,在派生类中完整定义。
抽象类的使用要求
抽象类不能实例化,即不能创建抽象类 的对象 抽象类只作为基类被继承 能够定义指向抽象类的指针或引用
改为:
private:?
三. 虚函数
在程序执行期间,如何通过基类指针或基类引用访问派生类中覆盖基 类的同名成员函数?这时可以利用C++中提供的“虚函数”技术。 如:
PC=&ds; //基类指针指向派生类对象 cout<<“4: ”<<PC->Area()<<endl; //访问基类中的Area()
};
Circle::Circle(int a,int b,double r): Point(a,b) { SetRadius( r ); } double Circle::Area() 类外定义的虚函数 { return 3.14159 * radius * radius; }
类外定义的虚函数 void Circle::Print() const { cout << "Center = "; Point::Print(); cout << "; Radius = " << radius << endl; }
Point类
实现每一个虚函数
};
Point::Point( int a, int b ) { SetPoint( a, b ); }
Point类成员函数定义
void Point::SetPoint( int a, int b ) { x = a;y = b; } void Point::Print() const { cout << '[' << x << ", " << y << ']'; }
例12-1:公有派生类对象替换基类对象
int main() { Cylinder ds(1,10); Circle cs(10),*PC, &pd=ds; cout<<“1: ”<<cs.Area()<<endl; //访问基类中的Area() cout<<“2: ”<<ds.Area()<<endl; //访问派生类中的Area() cs=ds; //派生类对象替换基类对象 cout<<“3: ”<<cs.Area()<<endl; //访问基类中的Area() PC=&ds; //基类指针指向派生类对象 cout<<“4: ”<<PC->Area()<<endl; //访问基类中的Area() cout<<“5: ”<<pd.Area()<<endl; //访问基类中的Area() return 0; }
五.运算符重载
重载运算符主要用于对类对象的操作。 例如:2个复数类对象的加法, c=a+b; 运算符重载函数的定义格式:
class Point : public Shape {
int x, y; //点的x和y坐标 public: Point( int = 0, int = 0 ); // 构造函数 void SetPoint( int, int ); // 设置坐标 int GetX() { return x; } // 取x坐标 int GetY() { return y; } // 取y坐标 virtual double Area() { return 0.0; } virtual void PrintShapeName() const { cout << "Point: "; } virtual void Print() const; //输出点的坐标
测试函数 int main() { Point point(30,50); Circle circle(120,80,10.0); point.PrintShapeName(); point.Print(); 结果: cout << endl; Point: circle.PrintShapeName(); Circle: circle.Print(); Shape *arrayOfShapes[ 2 ]; //指向抽象类的指针 arrayOfShapes[ 0 ] = &point; arrayOfShapes[ 1 ] = &circle; arrayOfShapes[ 0 ]-> PrintShapeName(); arrayOfShapes[ 1 ]-> PrintShapeName(); }