虚拟继承时:
1、当虚基类的成员通过不同派生路径继承,并没有被改写时,派生类对该虚基类成员的调用,不存在二义性。
2、如果一个是虚基类成员,另一个是虚基类派生类成员,也不存在二义性。(改写后的派生类成员优先级高于共享虚基类成员)
3、如果两个成员都来自后续派生类,那么存在二义性。
如下fun1,fun2,fun3分别代表以上三种情况
class A
{
public:
A(){}
void fun1(){cout<<"A1"<<endl;}
void fun2(){cout<<"A2"<<endl;}
void fun3(){cout<<"A3"<<endl;}
};
//virtual inheritance
class B: virtual public A
{
public:
B(){}
void fun3(){cout<<"B3"<<endl;}
void fun2(){cout<<"B2"<<endl;}
};
//virtual inheritance
class C :virtual public A
{
public:
C(){}
void fun3(){cout<<"C3"<<endl;}
};
class D : public C, public B
{
public:
D(){}
};
int main()
{
D d;
d.fun1(); //ok, call A::fun1()
d.fun2(); //ok, call B::fun2()
d.fun3(); //error, ambiguous, could be B::fun(), or C::fun()
d.C::fun3(); //ok
return 0;
}
非虚拟继承时,以上三种情况均存在二义性,因为每个继承得到的实例优先级是一样的。
class A
{
public:
A(){}
void fun1(){cout<<"A1"<<endl;}
void fun2(){cout<<"A2"<<endl;}
void fun3(){cout<<"A3"<<endl;}
};
class B: public A
{
public:
B(){}
void fun3(){cout<<"B3"<<endl;}
void fun2(){cout<<"B2"<<endl;}
};
class C: public A
{
public:
C(){}
void fun3(){cout<<"C3"<<endl;}
};
class D : public C, public B
{
public:
D(){}
};
int main()
{
D d;
d.fun1(); //error, ambiguous
d.fun2(); //error, ambiguous
d.fun3(); //error, ambiguous
d.C::fun3(); //ok
return 0;
}
可以通过使用类域操作符来限定访问,消除二义性。
当编译器解析一个类成员时,按如下顺序:
1、局部域
2、类域
3、基类域
若哪一步解析成功,不会继续解析后面的,所以即使前面步骤里解析出来的成员无法访问,也不会继续解析后面步骤里可以访问的成员。
如下例子中,C里的fun2被解析成为父类中的private成员,尽管全局的fun2可以访问。
class A
{
private:
void fun2(){cout<<"A2"<<endl;}
};
void fun2(){}
class C: public A
{
void test()
{
fun2(); //error, A::fun2 is private
::fun2(); //ok
}
};
如下例子中,虽然A::fun2()是private,无法访问,B::fun2()是public,可以访问,但仍然是二义性。
class A
{
private:
void fun2(){cout<<"A2"<<endl;}
};
class B
{
public:
void fun2(){cout<<"B2"<<endl;}
};
class C: public A, public B
{
};
int main()
{
C c;
c.fun2(); //error
return 0;
}