1,派生类具有基类的所有成员 。
2,派生类的构造函数自动执行基类的构造函数,
且基类的构造函数先执行。基类构造函数的参数由派生类传递。
3,派生类中可对已有的成员进行重新定义。
4.可定义多重派生类。且可利用虚基类使派生类只保持一个基类的成员拷贝 。
第十讲 虚函数与多态性
8.1 多态的概念
8.2 虚函数
§ 8.1 多态的概念
§ 8.1 多态的概念什么是多态性指在一般类中定义的操作在被继承类继承后可以有不同的表现形式。
例如:图形的求面积类,shape
操作:求面积类:椭圆操作:求面积类:长方形操作:求面积解决的方法,用虚函数实现动态联编
§ 8.1 多态的概念例 1:没有用多态性的例题
#include<iostream.h>
class A {
public:
void print( ) { cout << "A "; } };
class B:public A { //定义派生类
public:
void print( ) { cout <<,B,; } }; //重新定义函数
void main( ) {
A a,*p;
B b;
p= &a; p->print( ); //访问基类函数
p= &b; p->print( ); //希望访问派生类函数
}
结果,AA
§ 8.2 虚函数虚函数的定义方法在基类中,在要被定义成虚函数的函数前加关键字 virtual
例如,virtual void print( );
注意,1,关键字是 virtual
2,放在最前面,函数的类型不能忘,
如 virtual void print( ),中的 void
3,可只在基类中的函数加 virtual,在派生类中不加,
但为了便于阅读,可在所有要声明为虚函数的前面都加 virtual
4,只需要在类定义文件,即头文件中的虚函数加
virtual,在 cpp 文件中就不要再加 virtual
§ 8.2 虚函数例 2:用多态性解决例 1中的问题
#include<iostream.h>
class base {
public:
virtual void print( ) { cout <<,base,; } }; //定义了虚函数
class first:public base { //定义派生类
public:
void print( ) { cout <<,first,; } }; //重新定义 print( )
class second:public base { //定义派生类
public:
void print( ) { cout << " second "; } }; //重新定义 print( )
§ 8.2 虚函数
void main( ) {
base ob1,*p; //定义基类的对象
first ob2; //定义派生类的对象
second ob3; //定义派生类的对象
p= &ob1; //p 指向基类对象 ob1
p->print( ); // 调基类成员函数
p= &ob2; // p 指向子类对象 ob2
p->print( ); // 动态联编,调子类成员函数
p= &ob3; // p 指向子类对象 ob3
p->print( ); // 动态联编,调子类成员函数
}
结果,base
first
second
§ 8.2 虚函数例 2的相关内容总结
1,基类中的虚函数是由 virtual 定义的,
virtual void print( );
2,在定义了虚函数之后,只要定义一个基类的指针,就可以调用子类的函数,
p= &ob2; // p 指向子类对象 ob2
p->print( ); // 动态联编,调子类成员函数
3,要实现动态联编,子类中的函数必需与基类的函数形式一致,若在子类中对虚函数的形式进行了改变,则子类中的函数将失去多态性 (详细见下例 )
§ 8.2 虚函数例 3,虚函数重载讨论
#include<iostream.h>
class base {
public:
virtual void f1( ) { cout << "f1 function of base \n "; } //定义虚函数
virtual void f2( ) { cout << " f2 function of base \n "; } //定义虚函数
void f3( ) { cout << " f3 function of base \n "; } //定义一般虚函数
};
class derive,public base {
public:
void f1( ) { cout << " f1 function of derive \n "; } //重新定义虚函数
void f2(int x ) {cout << " f2 function of derive \n "; } //重新定义虚函数,
//由于形式改变,失去
//虚函数功能
void f3( ) { cout << " f3 function of derive \n "; } //重新定义一般函数
};
§ 8.2 虚函数
void main( )
{ base ob1,*p; //定义基类对象
derive ob2; //定义派生类对象
p=&ob1;
p->f1( ); //调基类函数
p->f2( ); //调基类函数
p->f3( ); //调基类函数
p=&ob2;
p->f1( ); //调子类函数,多态性
p->f2( ); //调基类函数,由于 f2的形式改变
p->f3( ); //调基类函数,由于没有定义为虚函数
}
结果,f1 function of base
f2 function of base
f3 function of base
f1 function of derive
f2 function of base
f3 function of base
§ 8.2 虚函数例 3的相关内容总结
1,在定义了虚函数之后,只要定义一个基类的指针,就可以调用子类的函数,
p= &ob2; // p 指向子类对象 ob2
p->f1( ); // 动态联编,调子类成员函数
2,若在子类中对虚函数的形式进行了改变,则子类中的函数将失去多态性,
p=&ob2;
p->f2( ); //调基类函数,而非派生类函数(由于 f2形式改变)
3,若基类中的函数没有声明为虚函数,则子类中的函数不具有多态性,
p=&ob2;
p->f3( ); //调基类函数
§ 8.2 虚函数例 4,多继承虚函数作用讨论
base,有虚函数
bddrive,有多态性
derive,有多态性 bbderive:
base对象调用,有多态性
base1对象调用,无多态性
base1,无虚函数
§ 8.2 虚函数
#include<iostream.h>
class base { //定义基类 base
public:
virtual void f( ) { cout << " base \n "; } }; //定义虚函数
class derive,public base { //定义 base 的派生类
public:
void f( ) { cout << " derive \n "; } }; //重新定义虚函数
class base1 { //定义基类 base1
public:
void f( ) { cout << " base1 \n "; } }; //定义一般函数
class bdderive,public derive { //定义派生类 derive的派生类 bdderive
public:
void f( ) { cout << " bdderive \n "; } }; //重新定义虚函数
class bbderive,public base,public base1 { //定义激类 base 和 base1的派生类
public:
void f( ) { cout << " bbderive \n "; } };
§ 8.2 虚函数
void main( ) {
base b,*p; //定义基类 base 的对象
base1 *p1; //定义基类 base1 的对象
bdderive bd; //定义派生类 bdderive 的对象
bbderive bb; //定义派生类 bbderive 的对象
p=&b;
p->f( ); //调 base基类函数
p=&bb;
p->f( ); //调子类函数,多态性
p1=&bb;
p1->f( ); //调 base1基类函数,无多态性
p=&bd;
p->f( ); //调子类函数,多态性
}
结果,base
bbderive
base1
bddrive
§ 8.2 虚函数例 4的相关内容总结
1,在定义了虚函数之后,其多态性在派生类中得到延伸。
2,在多重继承中,若某一基类无虚函数定义,则由该基类创建的对象不具有多态性,
base1 *p; //基类 base1中没有定义 f( )为虚函数
p=&bb;
p->f( ); //调基类函数,无多态性
§ 8.2 虚函数例 5,多态性的应用,考虑哺乳动物类 mammal,它有派生类狗 dog 和猫 cat,基类中有虚函数 speak( ),类层次如下图。
哺乳动物 mammal:虚函数 speak( )
猫,speak( ),多态狗,speak( ),多态
§ 8.2 虚函数
//基类和派生类定义
class mammal {
public,
virtual void speak( )
{ cout << "Mammal speak,\n "; } };
class dog,public mammal {
public:
void speak( ){ cout << " Woof!\n "; } };
class cat,public mammal {
public:
void speak( ){ cout << " Meow!\n "; } };
§ 8.2 虚函数
void main( ) {
mammal *p; //定义基类对象的指针
int choice;
do { //循环体
cout << "enter your choice ";
cout << "(0:exit,1:cat,2:dog),";
cin >> choice;
switch(choice) {
case 1,p=new cat; break;
case 2,p=new dog; break;
default,p=new mammal; }
p->speak( );
} while (choice!=0); //循环体结束
}
§ 8.2 虚函数结果,enter your choice(0:exit,1:cat,2:dog),1
Meow!
enter your choice(0:exit,1:cat,2:dog),2
Woof!
enter your choice(0:exit,1:cat,2:dog),1
Meow!
enter your choice(0:exit,1:cat,2:dog),3
Mammal speak!
enter your choice(0:exit,1:cat,2:dog),0
Press any key to continue
§ 8.2 虚函数例 5的相关内容总结
1,动态联编的体现,在程序中
p->speak( );
在编译时不知道要调用哪一个 speak(),
只有在 choice输入后才决定,由于定义了虚函数,系统会决定调用哪一个函数,
2,应用 do…while 构造一个多选择的循环体,
具有实际应用意义,
§ 8.2 虚函数例 6,多态性的应用,计算图形面积,其中 shape为一般有关图形的特征描述,是基类。类 circle代表“圆”,square代表“正方形”。
shape:虚函数 area()
square,area( ),多态circle,area( ),多态
§ 8.2 虚函数例 6,计算图形面积
class shape {
public,
virtual float area( )
{ return 0.0; } };
class circle,public shape {
public:
circle(float a=0) { r=a;}
float area( ){ return 3.14*r*r; }
private:
float r; };
class square,public shape {
public:
square(float b=0) { s=b; }
float area( ){ return s*s; }
private:
float s; };
§ 8.2 虚函数
#include<iostream.h>
void main( ) {
shape *p; //定义基类指针
circle c1(1.0); //创建对象 c1
square s1(2.0); //创建对象 s1
p=&c1; //基类指针指向 派生类对象 c1
cout <<,Area:,<< p->area( ) << endl; //调用派生类 circle的成员
p=&s1; //基类指针指向 派生类对象 s1
cout << "Area," << p->area( ) << endl; //调用派生类 square的成员
}
结果,Area,3.14
Area,4.0
2,在定义了虚函数之后,其多态性在派生类中得到延伸。
1,基类中的虚函数是由 virtual
定义的,virtual void print( );
3,在定义了虚函数之后,只要定义一个基类的指针,就可以调用子类的函数,
base *p;
derived obj;
p= &obj; // p 指向对象派生类 obj
p->print( ); // 动态联编,
//调子类成员函数
1。虚函数与多态性有什么关系?
2。虚函数在派生类中的特征是否被继承?
3。在例 6中关于 shape的类层次中,
增加派生类“椭圆”,它有私有数据 a和 b,分别表示椭圆的长短轴。试用虚函数定义一个计算椭圆面积的函数。