第 16讲 多态性与虚函数
教学目的与要求:
了解虚函数的作用和功能 。
掌握虚函数实现多态性 。
教学内容提要:
1,多态性的概念;
2,函数重载;
3,虚函数;
教学重点:虚函数的声明和使用 。
教学难点:虚函数的声明和使用 。
教学进度,P162,P200~P212
教学过程:
16.1 多态性概述多态 就是指不同的对象接受到相同的消息时产生不同的响应动作,即对应相同的函数名,却执行了不同的函数体 (当然,这些函数体还是要事先定义好,以便调用 )。
种把程序标示符与和一个存储地址相联系的过程,称为 联编
(binding,又译为绑定 )。
16.1.1 多态的分类
C++中的多态性可以分为四类,
参数多态:
包含多态:指在基类及其派生类族中同名函数的不同函数实现,及其在运行时的不同响应。
重载多态:函数重载强制多态:不同类型的数据类型进行混合运算时要进行的强制类型转换。
前面两种统称为通用多态,而后面两种统称为专用多态。
16.1.2多态的实现多态从实现的角度来讲可以划分为两类,
编译时的多态 和 运行时的多态 。
编译时的多态 是通过静态联编来实现的 。 静态联编就是在编译阶段完成的联编 。 编译时多态性主要是通过函数重载和运算符重载实现的 。
运行时的多态 是用动态联编实现的。动态联编是运行阶段完成的联编。运行时多态性主要是通过虚函数来实现的。
16.2 普通成员函数重载在 C++语言中,只有在声明函数原型时形式参数的个数或者对应位置的类型不同,两个或更多的函数就可以共用一个名字 。 这种在同一作用域中允许多个函数使用同一函数名的措施被称为重载 ( overloading) 。 函数重载是 C++程序获得多态性的途径之一 。
16.2.1 函数重载的方法函数重载要求编译器能够唯一地确定调用一个函数时应执行哪个函数代码,既采用哪个函数实现。确定函数实现时,
要求从函数参数的个数和类型上来区分。这就是说,进行函数重载时,要求同名函数在参数个数上不同,或者参数类型不同。否则,将无法实现函数重载。
#include <iostream.h>
const double PI=3.1415;
double length(float r)
{
return 2*PI*r;
}
double length(float x,float y)
{
return 2*(x+y);
}
例 16.1:用重载函数实现求圆和矩形的周长。
void main()
{
float a,b,r;
cout<<”输入圆半径:”;
cin>>r;
cout<<“圆周长:” <<length(r)<<endl;
cout<<”输入矩形长和宽:”;
cin>>a>>b;
cout<<”矩形周长:” <<length(a,b)<<endl;
}
16.2.2 函数重载的表示形式普通成员函数重载可表达为两种形式:
1,在一个类说明中重载例如,Show ( int,char ) ;
Show ( char *,float ) ;
2,基类的成员函数在派生类重载 。 有 3种编译区分方法
( 1) 根据参数的特征加以区分例如,Show ( int,char ) 与
Show ( char *,float )
不是同一函数,编译能够区分
( 2) 使用,,:,加以区分例如,A,,Show ( )
有别于 B,,Show ( )
( 3) 根据类对象加以区分例如,Aobj.Show ( )调用 A,,Show ( )
Bobj.Show ( )调用 B,,Show ( )
#include <iostream.h>
class point
{ int x,y;
public:
point(int a,int b)
{x=a;y=b}
float area(){return 0;}
};
class circle:public point
{ int radius;
public:
circle(int a,int b,int r):point(a,b)
{ radius=r;}
float area()
{ return 3.1416*radius*radius;}
};
void main()
{
point p(20,20);
circle c(8,8,30);
cout<<p.area()<<endl;
cout<<c.area()<<endl;
cout<<c.point::area()<<endl;
}
6.3 虚函数虚函数提供了一种更为灵活的多态性机制。虚函数允许函数调用与函数体之间的联系在运行时才建立,也就是在运行时才决定如何动作,即所谓的动态联编。
6.3.1 虚函数的引入例 16.2 虚函数的引例 。
#include<iostream.h>
class A{
public:
void show(){ cout<<"A"; }
};
class B:public A {
public:
void show(){ cout<<"B"; }
};
main()
{ A a,*pc;
B b;
pc=&a; pc->show();
pc=&b; pc->show();
return 0;
}
6.3.2 虚函数的作用和定义
1.虚函数的作用虚函数同派生类的结合可使 C++支持运行时的多态性,实现了在基类定义派生类所拥有的通用接口,而在派生类定义具体的实现方法,即常说的“同一接口,多种方法”,它帮助程序员处理越来越复杂的程序。
例 6.3 虚函数的作用 。
#include<iostream.h>
class Base {
public:
Base(int x,int y)
{ a=x; b=y; }
virtual void show() //定义虚函数 show()
{ cout<<"Base----------\n"; cout<<a<<" "<<b<<endl;}
private:
int a,b; };
class Derived,public Base {
public:
Derived(int x,int y,int z):Base(x,y){c=z; }
void show() //重新定义虚函数 show()
{ cout<< "Derived---------\n"; cout<<c<<endl;}
private:
int c;
};
void main()
{ Base mb(60,60),*pc;
Derived mc(10,20,30);
pc=&mb;
pc->show(); //调用基类 Base的 show()版本
pc=&mc;
pc->show(); //调用派生类 Derived的 show()版本
}
程序运行结果如下,
Base---------
60 60
Derived--------
30
2,虚函数的定义定义虚函数的方法如下,
virtual 函数类型 函数名 (形参表 )
{ 函数体 }
例 6.4 虚函数的定义举例。
#include<iostream.h>
class Grandam {
public:
virtual void introduce_self() // 定义虚函数 introduce_self()
{ cout<<"I am grandam."<<endl; }
};
class Mother:public Grandam {
public:
virtual void introduce_self() // 重新定义虚函数 introduce_self()
{ cout<<"I am mother."<<endl;}
};
class Daughter:public Mother {
public:
virtual void introduce_self() // 重新定义虚函数 introduce_self()
{ cout<<"I am daughter."<<endl;}
};
void main()
{ Grandam *ptr;
Grandam g;
Mother m;
Daughter d;
ptr=&g;
ptr->introduce_self();//调用基类 Grandam的 introduce_self()
ptr=&m;
ptr->introduce_self();//调用派生类 Mother的 introduce_self()
ptr=&d;
ptr->introduce_self(); //调用派生类 Daughter的 introduce_self()
}
16.3.3 使用虚函数时应注意:
( 1) 在类体系中访问一个虚函数时,应使用指向基类类型的指针或对基类类型的引用,以满足运行时多态性的要求 。 当然也可以像调用普通成员函数那样利用对象名来调用一个函数 。
( 2) 在派生类中重新定义虚函数时,必须保证该函数的值和参数与基类中的说明完全一致,否则就属于重载 ( 参数不同 ) 或是一个错误 ( 返回值不同 ) 。
( 3) 若在派生类中没有重新定义虚函数,则该类的对象将使用其基类中的虚函数代码 。
( 4)虚函数必须是类的一个成员函数,不能是友元,但它可以是另一个类的友元。另外,虚函数不得是一个静态成员。
例 16.5
#include <iostream.h>
class B0 //基类 B0声明
{public,//外部接口
virtual void display( )
{cout<<"B0::display( ) "<<endl;}
};
class B1,public B0 //公有派生
{ public:
void display( )
{ cout<<"B1::display( ) "<<endl; }
};
class D1,public B1 //公有派生
{ public:
void display( )
{ cout<<"D1::display( ) "<<endl; }
};
void fun(B0 *ptr)
{
ptr->display( ) ;
}
void main( )
{ B0 b0,*p;
B1 b1;
D1 d1;
p=&b0;
fun(p);
p=&b1;
fun(p);
p=&d1;
fun(p);
}
16.3.4虚函数与重载函数的比较
( 1) 重载函数要求相同函数名称,并有不同的参数序列;而虚函数则要求这三项 ( 函数名,返回值类型和参数序列 ) 完全相同;
( 2) 重载函数可以是成员函数或友员函数,而虚函数只能是成员函数;
( 3) 重载函数的调用是以所传递参数序列的差别作为调用不同函数的依据;虚函数是根据对象的不同去调用不同类的虚函数;
( 4)虚函数在运行时表现出多态功能,这是 C++
的精髓;而重载函数则在编译时表现出多态性。
小结
1、什么是多态性,什么是静态联编和动态联编
2、派生类中的函数重载
3、虚函数的声明和使用作业:
P219~220 5.12,5.14
教学目的与要求:
了解虚函数的作用和功能 。
掌握虚函数实现多态性 。
教学内容提要:
1,多态性的概念;
2,函数重载;
3,虚函数;
教学重点:虚函数的声明和使用 。
教学难点:虚函数的声明和使用 。
教学进度,P162,P200~P212
教学过程:
16.1 多态性概述多态 就是指不同的对象接受到相同的消息时产生不同的响应动作,即对应相同的函数名,却执行了不同的函数体 (当然,这些函数体还是要事先定义好,以便调用 )。
种把程序标示符与和一个存储地址相联系的过程,称为 联编
(binding,又译为绑定 )。
16.1.1 多态的分类
C++中的多态性可以分为四类,
参数多态:
包含多态:指在基类及其派生类族中同名函数的不同函数实现,及其在运行时的不同响应。
重载多态:函数重载强制多态:不同类型的数据类型进行混合运算时要进行的强制类型转换。
前面两种统称为通用多态,而后面两种统称为专用多态。
16.1.2多态的实现多态从实现的角度来讲可以划分为两类,
编译时的多态 和 运行时的多态 。
编译时的多态 是通过静态联编来实现的 。 静态联编就是在编译阶段完成的联编 。 编译时多态性主要是通过函数重载和运算符重载实现的 。
运行时的多态 是用动态联编实现的。动态联编是运行阶段完成的联编。运行时多态性主要是通过虚函数来实现的。
16.2 普通成员函数重载在 C++语言中,只有在声明函数原型时形式参数的个数或者对应位置的类型不同,两个或更多的函数就可以共用一个名字 。 这种在同一作用域中允许多个函数使用同一函数名的措施被称为重载 ( overloading) 。 函数重载是 C++程序获得多态性的途径之一 。
16.2.1 函数重载的方法函数重载要求编译器能够唯一地确定调用一个函数时应执行哪个函数代码,既采用哪个函数实现。确定函数实现时,
要求从函数参数的个数和类型上来区分。这就是说,进行函数重载时,要求同名函数在参数个数上不同,或者参数类型不同。否则,将无法实现函数重载。
#include <iostream.h>
const double PI=3.1415;
double length(float r)
{
return 2*PI*r;
}
double length(float x,float y)
{
return 2*(x+y);
}
例 16.1:用重载函数实现求圆和矩形的周长。
void main()
{
float a,b,r;
cout<<”输入圆半径:”;
cin>>r;
cout<<“圆周长:” <<length(r)<<endl;
cout<<”输入矩形长和宽:”;
cin>>a>>b;
cout<<”矩形周长:” <<length(a,b)<<endl;
}
16.2.2 函数重载的表示形式普通成员函数重载可表达为两种形式:
1,在一个类说明中重载例如,Show ( int,char ) ;
Show ( char *,float ) ;
2,基类的成员函数在派生类重载 。 有 3种编译区分方法
( 1) 根据参数的特征加以区分例如,Show ( int,char ) 与
Show ( char *,float )
不是同一函数,编译能够区分
( 2) 使用,,:,加以区分例如,A,,Show ( )
有别于 B,,Show ( )
( 3) 根据类对象加以区分例如,Aobj.Show ( )调用 A,,Show ( )
Bobj.Show ( )调用 B,,Show ( )
#include <iostream.h>
class point
{ int x,y;
public:
point(int a,int b)
{x=a;y=b}
float area(){return 0;}
};
class circle:public point
{ int radius;
public:
circle(int a,int b,int r):point(a,b)
{ radius=r;}
float area()
{ return 3.1416*radius*radius;}
};
void main()
{
point p(20,20);
circle c(8,8,30);
cout<<p.area()<<endl;
cout<<c.area()<<endl;
cout<<c.point::area()<<endl;
}
6.3 虚函数虚函数提供了一种更为灵活的多态性机制。虚函数允许函数调用与函数体之间的联系在运行时才建立,也就是在运行时才决定如何动作,即所谓的动态联编。
6.3.1 虚函数的引入例 16.2 虚函数的引例 。
#include<iostream.h>
class A{
public:
void show(){ cout<<"A"; }
};
class B:public A {
public:
void show(){ cout<<"B"; }
};
main()
{ A a,*pc;
B b;
pc=&a; pc->show();
pc=&b; pc->show();
return 0;
}
6.3.2 虚函数的作用和定义
1.虚函数的作用虚函数同派生类的结合可使 C++支持运行时的多态性,实现了在基类定义派生类所拥有的通用接口,而在派生类定义具体的实现方法,即常说的“同一接口,多种方法”,它帮助程序员处理越来越复杂的程序。
例 6.3 虚函数的作用 。
#include<iostream.h>
class Base {
public:
Base(int x,int y)
{ a=x; b=y; }
virtual void show() //定义虚函数 show()
{ cout<<"Base----------\n"; cout<<a<<" "<<b<<endl;}
private:
int a,b; };
class Derived,public Base {
public:
Derived(int x,int y,int z):Base(x,y){c=z; }
void show() //重新定义虚函数 show()
{ cout<< "Derived---------\n"; cout<<c<<endl;}
private:
int c;
};
void main()
{ Base mb(60,60),*pc;
Derived mc(10,20,30);
pc=&mb;
pc->show(); //调用基类 Base的 show()版本
pc=&mc;
pc->show(); //调用派生类 Derived的 show()版本
}
程序运行结果如下,
Base---------
60 60
Derived--------
30
2,虚函数的定义定义虚函数的方法如下,
virtual 函数类型 函数名 (形参表 )
{ 函数体 }
例 6.4 虚函数的定义举例。
#include<iostream.h>
class Grandam {
public:
virtual void introduce_self() // 定义虚函数 introduce_self()
{ cout<<"I am grandam."<<endl; }
};
class Mother:public Grandam {
public:
virtual void introduce_self() // 重新定义虚函数 introduce_self()
{ cout<<"I am mother."<<endl;}
};
class Daughter:public Mother {
public:
virtual void introduce_self() // 重新定义虚函数 introduce_self()
{ cout<<"I am daughter."<<endl;}
};
void main()
{ Grandam *ptr;
Grandam g;
Mother m;
Daughter d;
ptr=&g;
ptr->introduce_self();//调用基类 Grandam的 introduce_self()
ptr=&m;
ptr->introduce_self();//调用派生类 Mother的 introduce_self()
ptr=&d;
ptr->introduce_self(); //调用派生类 Daughter的 introduce_self()
}
16.3.3 使用虚函数时应注意:
( 1) 在类体系中访问一个虚函数时,应使用指向基类类型的指针或对基类类型的引用,以满足运行时多态性的要求 。 当然也可以像调用普通成员函数那样利用对象名来调用一个函数 。
( 2) 在派生类中重新定义虚函数时,必须保证该函数的值和参数与基类中的说明完全一致,否则就属于重载 ( 参数不同 ) 或是一个错误 ( 返回值不同 ) 。
( 3) 若在派生类中没有重新定义虚函数,则该类的对象将使用其基类中的虚函数代码 。
( 4)虚函数必须是类的一个成员函数,不能是友元,但它可以是另一个类的友元。另外,虚函数不得是一个静态成员。
例 16.5
#include <iostream.h>
class B0 //基类 B0声明
{public,//外部接口
virtual void display( )
{cout<<"B0::display( ) "<<endl;}
};
class B1,public B0 //公有派生
{ public:
void display( )
{ cout<<"B1::display( ) "<<endl; }
};
class D1,public B1 //公有派生
{ public:
void display( )
{ cout<<"D1::display( ) "<<endl; }
};
void fun(B0 *ptr)
{
ptr->display( ) ;
}
void main( )
{ B0 b0,*p;
B1 b1;
D1 d1;
p=&b0;
fun(p);
p=&b1;
fun(p);
p=&d1;
fun(p);
}
16.3.4虚函数与重载函数的比较
( 1) 重载函数要求相同函数名称,并有不同的参数序列;而虚函数则要求这三项 ( 函数名,返回值类型和参数序列 ) 完全相同;
( 2) 重载函数可以是成员函数或友员函数,而虚函数只能是成员函数;
( 3) 重载函数的调用是以所传递参数序列的差别作为调用不同函数的依据;虚函数是根据对象的不同去调用不同类的虚函数;
( 4)虚函数在运行时表现出多态功能,这是 C++
的精髓;而重载函数则在编译时表现出多态性。
小结
1、什么是多态性,什么是静态联编和动态联编
2、派生类中的函数重载
3、虚函数的声明和使用作业:
P219~220 5.12,5.14