在上周的C++课中,我第一次接触到虚函数(virtual function)这个非常重要的概念
什么是virtual函数?
在某基类中声明为 virtual 并在一个或多个派生类中被重新定义的成员函数,用法格式为:virtual 函数返回类型 函数名(参数表) {函数体};实现多态性,通过指向派生类的基类指针或引用,访问派生类中同名覆盖成员函数。
简单地说,那些被virtual关键字修饰的成员函数,就是虚函数。虚函数的作用,用专业术语来解释就是实现多态性(Polymorphism),多态性是将接口与实现进行分离;用形象的语言来解释就是实现以共同的方法,但因个体差异,而采用不同的策略。
看一段百度百科中的代码:
#include<iostream>
using namespace std;
class A
{
public:
void print()
{
cout<<"This is A"<<endl;
}
};
class B : public A
{
public:
void print()
{
cout<<"This is B"<<endl;
}
};
int main()
{
//为了在以后便于区分,我这段main()代码叫做main1
A a;
B b;
a.print();
b.print();
return 0;
}
输出的结果分别是“This is A”、“This is B”。
通过class A和class B的print()这个接口,可以看出这两个class因个体的差异而采用了不同的策略,但这并不是多态性行为(使用的是不同类型的指针),没有用到虚函数的功能。
现在把main()处的代码改一改。
int main()
{
//main2
A a;
B b;
A *p1 = &a;
A *p2 = &b;
p1->print();
p2->print();
return 0;
}
这时的结果却是:
This is A
This is A
很明显这不是我们想要的结果,但由于指针类型为A,程序已自动进行了静态绑定,所以调用了A类中的函数,为了解决这个问题,这时我们就需要用到虚函数
class A
{
public:
virtual void print(){cout<<"This is A"<<endl;}
};
class B : public A
{
public:
void print(){cout<<"This is B"<<endl;}
};
这时A中的成员函数print()已经称为了虚函数,而只需要将基类定义为虚函数,其派生类中的同名同参函数也会自动变为虚函数,所以B中不需要再加上virtual关键字修饰
现在重新运行main2的代码,这样输出的结果就是This is A和This is B了。
因此,virtual函数就是来帮助我们在C++程序类的继承中,实现函数的重写(Override)
可类比java,重写是子类对父类的允许访问的方法的实现过程进行重新编写, 返回值和形参都不能改变。即外壳不变,核心重写!
virtual函数与静态绑定
再看一段代码:
#include <iostream>
using namespace std;
class Parent
{
public:
char data[20];
void Function1();
virtual void Function2(); // 这里声明Function2是虚函数
}parent;
void Parent::Function1()
{
printf("This is parent,function1\n");
}
void Parent::Function2()
{
printf("This is parent,function2\n");
}
class Child:public Parent
{
void Function1();
void Function2();
} child;
void Child::Function1()
{
printf("This is child,function1\n");
}
void Child::Function2()
{
printf("This is child,function2\n");
}
int main(int argc, char* argv[])
{
Parent *p; // 定义一个基类指针
if(_getch()=='c') // 如果输入一个小写字母c
p=&child; // 指向继承类对象
else
p=&parent; // 否则指向基类对象
p->Function1(); // 这里在编译时会直接给出Parent::Function1()的入口地址。
p->Function2(); // 注意这里,执行的是哪一个Function2?
return 0;
}
在实际执行过程中:
当输入小写字母c,得到结果
1 This is parent,function1
2 This is child,function2
第一行原因:p指针为Parent类型,在程序运行前已经与其function1函数进行静态绑定,所以就算p指向的是一个Child对象,还是会调用Parent类的成员函数
第二行原因:第二行中调用了子类的function2,完全是因为virtual 函数的功能,virtual实现了动态联编,它可以在运行时判断指针指向的对象,并自动调用相应的函数。
而当你输入一个非c的字符,其结果为
1 This is parent,function1
2 This is parent,function2
这向我们展示了,使用virtual函数可以使得程序只调用一个函数,却能根据不同情况实现不同的功能,这就是virual的用途所在。
参考文档:C++中的virtual