这里,我们要讨论下虚基类成员的可见性。
理论基础
假定类B定义了一个名为x的成员,D1和D2都是从B 虚继承 得到的,D继承了D1和D2,则通过D的对象使用x,有三种可能性:
- 如果在D1和D2中都没有x的定义,则x将被解析为B的成员,此时不存在二义性,一个D的对象只含有x的一个实例;
- 如果D1或D2中某一个成员定义了x,则同样没有二义性,派生类的x比虚基类中的x优先级更高;
- 如果D1和D2都定义了x,存在二义性;
具体例子
该例子是C++ Primer 18.24题,在这里我简单地做了些修改。
原题中D1::bar(char)以及D1::foo(char)存在char<->int间隐式类型转换的问题,
为了更加直观,我改成了string。
struct Base {
void bar(int i);
protected:
int ival;
};
struct D1 : virtual public Base {
public:
void bar(string i); // orig: char i
void foo(string i); // orig: char i
private:
char cval;
};
struct D2 : virtual public Base {
public:
void foo(int i);
protected:
int ival;
char cval;
};
class VMI : public D1, public D2 {};
题目中问在VMI类内部有哪些继承而来的成员可以无需限定符即可访问?
按照上述的规则,我们可以知道只有D1::foo()以及D2::ival。
深层次原理
- 派生类的作用域嵌套在直接基类的作用域中。
- 名字查找过程沿着集成体系自底向上进行,直到找到所需的名字。
- 在多重继承时,相同的查找过程在所有直接基类中同时进行。
实验代码
#include <iostream>
using namespace std;
struct Base {
void bar(int i) { cout << __FUNCTION__ << i << endl; }
protected:
int ival = 0;
};
struct D1 : virtual public Base {
public:
void bar(char i) { cout << __FUNCTION__ << i << endl; }
void foo(char i) { cout << __FUNCTION__ << i << endl; }
private:
char cval = 'a';
};
struct D2 : virtual public Base {
public:
void foo(int i) { cout << __FUNCTION__ << i <<endl; }
protected:
int ival = 1;
char cval = 'b';
};
class VMI : public D1, public D2 {
public:
void verify() {
cout << ival << endl;
bar('x');
bar(42);
}
};
int main()
{
VMI vmi;
vmi.verify();
}