11.4 虚函数引入的二义性

    第10章中讨论了继承时的二义性问题,同样虚函数也存在二义性问题。本节分别从多基派生、共同基类(非虚基派生)和共同基类(虚基派生)3个方面进行讨论。

    11.4.1 多基派生

    分析如图11.1所示的DAG图,各类的定义如下所示。


    <———————————————-文件名:ABCdef.h——————————————-> #include<iostream> using namespace std; class A { public: virtual void a()//虚函数 { cout<<"a()in A"<<endl; } virtual void b()//虚函数 { cout<<"b()in A"<<endl; } virtual void c()//虚函数 { cout<<"c()in A"<<endl; } }; class B { public: virtual void a()//虚函数 { cout<<"a()in B"<<endl; } virtual void b()//虚函数 { cout<<"b()in B"<<endl; } void c()//非虚函数 { cout<<"c()in B"<<endl; } void d()//非虚函数 { cout<<"d()in B"<<endl; } }; class C:public A,public B { public: virtual void a()//虚函数,覆盖 { cout<<"a()in C"<<endl; } void c()//特殊 { cout<<"c()in C"<<endl; } void d()//非虚函数,隐藏 { cout<<"d()in C"<<endl; } };第11章已经将过,在使用类C对象名(或类C对象指针)调用成员函数b时(静态联编),编译器无法知道应该执行从类A继承来的函数版本还是应执行从类B继承而来的函数版本,会出现二义性错误,如下所示。 C obc; C*pC=&obc; obc.b();//错误,二义性 pC->b();//错误,二义性

    11.4 虚函数引入的二义性 - 图1

    图 11.1 多基派生

    虚函数访问方式如代码11.12所示,即用基类指针作为访问接口。

    代码11.12 虚函数访问方式VirtualFuncAccess


    <————————————-文件名:example1112.cpp——————————————> 01 #include"ABCdef.h" 02 int main() 03 { 04 C obc;//声明一个派生类对象obc 05 A*pA=&obc;//用派生类对象obc的地址为A类指针赋值 06 pA->a(); 07 pA->b(); 08 pA->c(); 09 B*pB=&obc;//用派生类对象obc的地址为B类指针赋值 10 pB->a(); 11 pB->b(); 12 pB->c(); 13 pB->d(); 14 return 0; 15 }

    输出结果如下所示。


    a()in C b()in A c()in C a()in C b()in B c()in B d()in B

    【代码解析】从代码11.12可以看出,对上述类定义而言,可以使用类A指针或类B指针指向类C对象,3个类(A、B、C)中的a函数都为虚函数,对虚函数来说,派生类中的定义会覆盖基类中的定义。因此代码第5行和第10行的“pA->a();”和“pB->a();”都输出了“a()in C”。

    但对“pA->b();”和“pB->b();”操作来说,编译器在类A和类B定义中b()都为虚函数,但由于在类C中没有对b()进行覆盖,所以编译器无法决定执行哪个函数版本,采用静态联编调用方式,输出结果分别为“b()in A”和“b()in B”。

    3个类中函数c稍微特殊一点,在类A中c()为虚函数,而在类B中c()为普通函数,虽然在类C中重定义(不是覆盖)了c函数,此时“pA->c();”和“pB->c();”操作的输出结果取决于指针所在类定义中c函数是否为虚函数,因此,输出结果分别为“c()in C”和“c()in B”。

    注意

    类B和类C中都定义了非虚函数d,这种情况下,派生类C中的d()会隐藏基类B中的d(),此时,使用类C对象或指针调用的将是类C中定义的d(),关于覆盖和隐藏的内容稍后会进行详细的介绍。