目录
-
5.4 虚函数
虚函数是重载的另一种表现形式,提供了运行时的多态机制
虚函数允许函数调用时与函数体之间的联系在运行时才建立,也就是动态联编
-
5.4.1 虚函数引入
【虚函数引入示例】
#include<iostream>
using namespace std;
class Base{
public:
Base(int sub_x,int sub_y){
x=sub_x;
y=sub_y;
}
void show(){
cout<<"调用基类Base的show()函数"<<endl;
cout<<"x = "<<x<<" y = "<<y<<endl;
}
private:
int x;
int y;
};
class Derived:public Base{
public:
Derived(int sub_x,int sub_y,int sub_z):Base(sub_x,sub_y){
z=sub_z;
}
void show(){
cout<<"调用派生类的show()函数"<<endl;
cout<<"z = "<<z<<endl;
}
private:
int z;
};
int main()
{
Base base(50,50),*ba; //定义基类对象base和对象指针ba
Derived derived(10,20,30); //定义派生类对象derived
ba=&base; //基类对象指针指向基类对象base
ba->show();
ba=&derived; //基类对象指针指向派生类对象derived
ba->show();
return 0;
}
【虚函数引入示例运行结果】
【虚函数引入示例说明】
ba=&derived,ba指向派生类对象,执行语句“ba->show()”调用的仍是基类的同名成员函数show
原因在于:C++中规定基类的对象指针可以指向它公有派生类对象,但只能访问派生类中从基类继承来的成员,而不能访问公有派生类中定义的成员
【虚函数示例】
#include<iostream>
using namespace std;
class Base{
public:
Base(int sub_x,int sub_y){
x=sub_x;
y=sub_y;
}
virtual void show(){ //在基类中定义虚函数show
cout<<"调用基类Base的show()函数"<<endl;
cout<<"x = "<<x<<" y = "<<y<<endl;
}
private:
int x;
int y;
};
class Derived:public Base{
public:
Derived(int sub_x,int sub_y,int sub_z):Base(sub_x,sub_y){
z=sub_z;
}
virtual void show(){ //在派生类中定义虚函数show
cout<<"调用派生类的show()函数"<<endl;
cout<<"z = "<<z<<endl;
}
private:
int z;
};
int main()
{
Base base(50,50),*ba; //定义基类对象base和对象指针ba
Derived derived(10,20,30); //定义派生类对象derived
ba=&base; //基类对象指针指向基类对象base
ba->show();
ba=&derived; //基类对象指针指向派生类对象derived
ba->show();
return 0;
}
【虚函数示例运行结果】
5.4.2 虚函数的定义
1、虚函数的定义
【定义】虚函数就是在基类被关键字virtual说明,并在派生类中重新定义的函数
【作用】允许在派生类中重新定义与基类同名的函数,并且可以通过基类指针或引用来访问基类和派生类中的同名函数
【定义方法】
virtual 函数类型 函数名(形参表){
函数体
}
【说明】
(1)在派生类中重新定义时,其函数原型,包括函数类型、函数名、参数个数、参数类型的顺序都必须与基类中的原型完全相同
(2)若在基类中,只声明虚函数原型需要加上virtual,而在类外定义虚函数时,则不必再加virtual,注意常成员函数声明定义的区别
(3)当一个成员函数被定义为虚函数后,其派生类中符合重新定义虚函数要求的同名函数都自动成为虚函数
(4)如果在派生类中没有对基类的虚构函数重新定义,则公有派生类继承其直接按基类的虚函数,一个虚函数无论被继承多少次,仍然保持其虚函数的特性
(5)虚函数必须是所在类的成员函数,而不能是友元函数,也不能是静态成员函数,因为虚函数调用要靠特定的对象来决定激活哪个函数
(6)只有通过基类指针访问虚函数时才能获得运行时的多态性,而使用点运算符的方式调用虚函数属于静态联编
2、虚析构函数
不能声明虚构造函数,但可以声明虚析构函数
【虚析构函数引入示例】
主函数中用new运算符建立一个派生类的无名对象和定义一个基类的对象指针,并将无名对象的地址赋给这个基类对象指针,当调用delete运算符撤销无名对象时,系统只执行基类的析构函数,而不执行派生类的析构函数
#include<iostream>
using namespace std;
class A{
public:
~A(){
cout<<endl<<"调用基类B的析构函数\n";
}
};
class B:public A{
public:
~B(){
cout<<endl<<"调用派生类D的析构函数\n";
}
};
int main()
{
A *p;
p=new B;
delete p;
return 0;
}
【虚析构函数引入示例运行结果】
【虚析构函数示例】
虽然派生类的析构函数与基类的析构函数名字不同,但是如果将基类的析构函数定义为虚函数,由基类所派生类的所有派生类的析构函数都自动为虚函数
#include<iostream>
using namespace std;
class A{
public:
virtual ~A(){
cout<<endl<<"调用基类B的析构函数\n";
}
};
class B:public A{
public:
~B(){
cout<<endl<<"调用派生类D的析构函数\n";
}
};
int main()
{
A *p;
p=new B;
delete p;
return 0;
}
【虚析构函数示例运行结果】
【虚析构函数格式】
virtual ~类名();
3、虚函数与重载函数的关系
在派生类中重新定义基类的虚函数是重载的另一种形式,但不同一般的函数重载
【普通的函数重载】
其函数的参数或参数类型必须有所不同,函数的返回类型可以不同
【虚函数重载】
(1)要求函数名、返回类型、参数个数、参数的类型和顺序与基类中的虚函数原型完全相同
(2)如果返回类型相同,其余不同,报错;如果函数名相同,而参数个数,参数类型,参数顺序不同,系统将它视为普通函数重载,虚函数特性丢失
【示例】
#include<iostream>
using namespace std;
class Base{
public:
virtual void function1();
virtual void function2();
void function3();
};
class Derived:public Base{
public:
virtual void function1();
void function2(int x);
void function3();
};
void Base::function1(){
cout<<endl<<"Base->function1"<<endl;
}
void Base::function2(){
cout<<endl<<"Base->function2"<<endl;
}
void Base::function3(){
cout<<endl<<"Base->function3"<<endl;
}
void Derived::function1(){
cout<<endl<<"Derived->function1"<<endl;
}
void Derived::function2(int x){
cout<<endl<<"Derived->function2"<<endl;
}
void Derived::function3(){
cout<<endl<<"Derived->function3"<<endl;
}
int main()
{
Base ba,*bp;
Derived de;
bp=&de;
bp->function1();
bp->function2();
bp->function3();
}
【示例运行结果】
4、多重继承与虚函数
【示例】
#include<iostream>
using namespace std;
class Base1{
public:
virtual void fun(){
cout<<"---Base1---"<<endl;
}
};
class Base2{
public:
void fun(){
cout<<"---Base2---"<<endl;
}
};
class Derived:public Base1,public Base2{
public:
void fun(){
cout<<"---Derived---"<<endl;
}
};
int main()
{
Base1 *ba1;
Base2 *ba2;
Derived de;
ba1=&de;
ba1->fun(); //此处的fun为虚函数
ba2=&de;
ba2->fun(); //此处的为非虚函数
}
【示例运行结果】
-
5.4.3 纯虚函数和抽象类
【定义】纯虚函数是一个在基类中说明的虚函数,它在该基类中没有定义,但要求它的派生类中根据需要对他进行定义,或仍说明为纯虚函数。
【形式】
virtual 函数类型 函数名(参数表)=0;
【示例】
#include<iostream>
using namespace std;
class Area{
public:
virtual void calculate()=0; //纯虚函数
};
class Circle:public Area{
public:
Circle(double radius){
r=radius;
}
void calculate(){ //重新定义纯虚函数
cout<<endl<<"circle area = "<<3.14*r*r<<endl;
}
private:
double r;
};
class Square:public Area{
public:
Square(double edge){
e=edge;
}
void calculate(){ //重新定义纯虚函数
cout<<endl<<"square area = "<<e*e<<endl;
}
private:
double e;
};
int main()
{
Area *a1,*a2;
Circle c(10);
a1=&c;
a1->calculate();
Square s(10);
a2=&s;
a2->calculate();
return 0;
}
【示例运行结果】
【抽象类说明】
如果一个类至少有一个纯虚函数,称该类为抽象类
(1)抽象类中至少包含一个没有定义功能的纯虚函数,因此抽象类只能用作其他类的基类,不能建立抽象类的对象
(2)抽象类不能用作参数类型、函数返回值、或显示转换的类型
(3)抽象类可以声明指向抽象类的指针变量,此指针指向它的派生类,实现多态性
(4)在抽象类的派生类中没有重新说明纯虚函数,则该函数在派生类中仍然为纯虚函数,这个派生类仍还是一个抽象类