虚函数是一种非静态的成员函数,定义的格式为:
virtual <类型说明符> <函数名> ( <参数表> )
{
<函数体>
}
被该关键字virtual说明的成员函数是虚函数。如果某类中的一个成员函数被说明为虚函数,这便意味着该成员函数在派生类中可能存在着不同的实现。由于有虚函数,因此才可能进行动态联编,否则只能实现静态联编。在动态联编实现过程中,调用虚函数的对象是在运行时确定的,这才对于类对象的选择就是动态联编的多态性。
C++语言规定,虚函数应具有下面的四点特性:
1,要求派生类中的虚函数与基类中的虚函数具有相同的参数个数,对应的参数类型相同且返回类型相同。
2,基类中说明的虚函数具有自动向下传给它的派生类的性质。因此,对于派生类的虚函数,virtual说明可以省略。
3,只有非静态的成员函数才可以说明为虚函数。这是因为虚函数仅适用于具有继承关系的类的对象,因此,普通函数不能说明为虚函数。又因为静态成员函数不是属于某个对象的,而是属于某个类的,所以它不适于虚函数。另外,内联函数不能是虚函数,因为函数不能在运行时动态确定自己的内容,即使虚函数在类体内被定义,编译系统也将视它为非内联函数。
4,构造函数不能被说明为虚函数,因为对象在创建时它还没有确定内存空间,只有在构造后后才是一个类的实例。析构函数可以是虚函数,并且最好在动态联编时被说明为虚函数。
下面来看虚函数实现动态联编的程序例题:
<span style="font-size:18px;">#include <iostream>
using namespace std;
class A
{
public:
virtual void f1()
{ cout<<"A::f1() called.\n"; }//基类中定义的成员函数为虚函数
void f2()
{ f1(); }
};
class B:public A
{
public:
void f1()//派生类中的定义的虚函数
{ cout<<"B::f1() called.\n"; }
};
int main()
{
B b;
b.f2();//通过类的对象调用成员函数
return 0;
}
</span>
程序分析:类B是类A的派生类,f1()是这两个类中的虚函数。在主函数中,b.f2()调用类B中的f2()函数,实际上是调用A::f2()函数,而A::f2()函数体内又调用f1(),f1()又是类A和类B中说明的虚函数,因此产生动态联编,所以输出的结果为:B::f1() called.
再来看定义的类外函数参数参数的问题的程序例题:
<span style="font-size:18px;">#include <iostream>
using namespace std;
class B
{
public:
virtual void fn()
{
cout<<"In B.\n";
}
};
class D:public B
{
public:
virtual void fn()
{
cout<<"In D.\n";
}
};
void text(B &x)
{
x.fn();
}
int main()
{
B b;
text(b);
D d;
text(d);
return 0;
}</span>
程序分析:
类D公有继承类B,类D是类B的子类型。在类D和类B中都定义了虚函数,它们的名字,参数和返回值都相同。在主函数中,使用不同的类的对象作为实参调用text()函数。由于满足动态联编的条件,于是调用x.fn()进行动态联编。因此输出的结果为:In B.和In D.
需要注意的是,类外函数text()的形参要求是类B的引用或指针,同时要求类D是类B的子类型,才可能进行动态联编。如果形参是类B的对象,将不进行动态联编,因此,动态联编要求调用函数的是基类的对象引用或对象指针,而不是对象。
继续看动态联编的例子,关键还是虚函数的例子:
<span style="font-size:18px;">#include <iostream>
using namespace std;
class A
{
public:
A()
{}
virtual void fun()
{ cout<<"In A::fun().\n"; }
};
class B:public A
{
public:
B()
{ fun(); }
void g()
{ fun(); }
};
class C:public B
{
public:
C()
{}
void fun()
{ cout<<"In C::fun().\n"; }
};
int main()
{
C c;
c.g();
return 0;
}</span>
程序分析:
类B继承类A,类C继承类B,类A和类C中都定义了一个同名虚函数。在主函数中,创建类C的对象c时调用默认构造函数C()。派生类的默认构造函数将包含调用它的基类的默认构造函数,因此调用基类B的默认构造函数时,调用虚函数fun(),输出In A::fun().。在构造函数中调用虚函数,将采用静态联编,因为构造函数不是在运行时调用的。在执行c.g();语句时,调用类B的g()函数,又调用虚函数fun(),将实行动态联编,输出In C::fun().。
最后再来看一个使用指向类的成员函数的指针来调用虚函数的动态联编的例题:
<span style="font-size:18px;">#include <iostream>
using namespace std;
class A
{
public:
virtual void f()
{ cout<<"A::f() called.\n"; }
};
class B:public A
{
void f()
{ cout<<"B::f() called.\n"; }
};
int main()
{
B b;
A &r=b;
void (A::*pf)=A::f;
(r.*pf)();
return 0;
}</span>
这道题我无法解释了,输出的结果为:B::f() called.