一般说来,在派生类中对基类成员的访问应该是唯一的,但是,由于多继承情况下,可能造成对基类中某成员的访问出现了不唯一的情况,则称为对基类成员访问的二义性问题。 实际上,在上例已经出现过这一问题,回忆一下上例中(参照继承和派生(3)最后一个例子),派生类A的两基类B1和B2中都有一个成员函 数print()。如果在派生类中访问print()函数,到底是哪 一个基类的呢?于是出现了二义性。但是在上例中(三角继承)解决了这个问题,其办法是通过作用域运算符::进行了 限定。如果不加以限定,则会出现二义性问题。
菱形继承
#include <iostream>
using namespace std;
class A
{ public:
A(int i){a=i;cout<<"con. A\n";}
void print(){cout<<a<<endl;}
~A(){cout<<"des. A\n";}
private:
int a;
};
class B1 : public A
{ public:
B1(int i, int j) : A(i){b1=j;cout<<"con. B1\n";}
void print(){A::print(); cout<<b1<<endl;}
~B1(){cout<<"des. B1\n";}
private:
int b1;
};
class B2 : public A
{ public:
B2(int i, int j) : A(i){b2=j;cout<<"con. B2\n";}
void print(){A::print(); cout<<b2<<endl;}
~B2(){cout<<"des. B2\n";}
private:
int b2;
};
class C : public B1, public B2
{ public:
C(int i, int j, int k, int l, int m) : B1(i,j), B2(k,l), c(m) {cout<<"con. C"<<endl;}
void print() {B1::print(); B2::print(); cout<<c<<endl; }
~C(){cout<<"des. C"<<endl;}
private:
int c;
};
int main()
{ C c1(1,2,3,4,5);
c1.print();
}
重复继承--虚基类
• 下面的类D从类A继承两次,称为重复继承:
class A
{ int x; ...... };
class B: public A
{ ... };
class C: public A
{ ... };
class D: public B, public C
{ ... };
• 如果需要类D中只有一个x,则应把A定义为 B和C的虚基类:
class B: virtual public A {...};
class C: virtual public A {...};
class D: public B, public C {...};
• 上面的类D将包含两个x成员:B::x和C::x。
虚基类的构造函数
C++规定,虚基类子对象是由最派生类的构造 函数通过调用虚基类的构造函数进行初始化的。如 果一个派生类有一个直接或间接的虚基类,那么派生类的构造函数的成员初始列表中必须列出对虚基类构造函数的调用。如果未被列出,则表示使用该 虚基类的缺省构造函数来初始化派生类对象中的虚基类子对象。
从虚基类直接或间接继承的派生类中的 构造函数的成员初始化列表中都要列出这个虚基类构造函数 的调用。但是,只有用于 建立对象的那个最派生类的构造函数调用虚 基类的构造函数,而该派生类的基类中所列 出的对这个虚基类的构造函数调用在执行中 被忽略,这样便保证了对虚基类的对象只初 始化一次。
C++又规定,在一个成员初始化列表中 出现对虚基类和非虚基类构造函数的调用,则虚基类的构造函数先于非虚基类的构造函数的执行。
#include <iostream>
using namespace std;
class A
{ public:
A(const char *s)
{cout<<s<<endl;}
~A(){}
};
class B : virtual public A
{ public:
B(const char *s1, const char * s2) : A(s1) { cout<<s2<<endl;}
};
class C : virtual public A
{ public:
C(const char *s1,const char * s2) : A(s1) {cout<<s2<<endl;}
};
class D : public B, public C
{ public:
D(const char * s1,const char * s2,const char * s3,const char * s4): B(s1,s2),C(s1,s3), A(s1)//虚基类先于非虚基类
{ cout<<s4<<endl; }
};
void main()
{
D* ptr=new D("class A","class B","class C","class D");
delete ptr;
}
在派生类B和C中使用了虚基类,使得 建立的D类对象只有一个虚基类子对象。 在派生类B,C,D的构造函数的成员 初始化列表中都包含了对虚基类A的构造 函数。 在建立类D对象时,只有类D的构造 函数的成员初始化列表中列出的虚基类构 造函数被调用,并且仅调用一次,而类D 基类的构造函数的成员初始化列表中列出 的虚基类构造函数不被执行。这一点将从 该程序的输出结果可以看出。