C++存在多重继承,允许一个派生类同时继承多个基类。语法如下:
class D:public A,private B,proected C
{ };
多重继承容易产生几个问题:
一是由于继承的成员同名产生的二义性问题。比如类A,类B都有成员a,当类C继承了类A,类B时,在使用a成员时,会出现二义性,不知道应该引用A类的还是B类的,解决方法是指定类:
C c1;
c1.A::a = 3;
用基类名限制防止出错。
二是当基类与子类有相同成员时,比如类A,B,C,都有成员函数display;则在执行c1.display()时,调用的是子类的dispay,此外还有基类与子类的display含参的问题,这里需要辨别重载,覆盖,隐藏三个名词,具体请看博文:http://blog.csdn.net/xckkcxxck/article/details/70046146
三是当A类,B类又同时继承自F类时,假设F类有一个成员b,则c1.b = 1;是错误的,因为无法知道是从类A继承下来,还是类B继承下来。应当指定类F的直接派生类名:
c1.A::b=1;
四是当我们观察三问题时,还存在一个问题,就是如果一个派生类有多个直接基类,而这些直接基类又有一个共同的基类,则在最终的派生类中会保留该间接共同基类的多分重名成员,这样不仅占用了较多的存储空间,同时增加了访问成员时的困难,所以C++提供了虚基类,使得在继承间接基类时只保留一份数据成员。
现举例说明:注意下面的程序同样说明了带参的构造函数的初始化过程。
class F
{ F(int i){ } //基类构造函数,有一个参数
...
};
class A:virtual public F //F作为A的一个虚基类
{ A(int n):F(n) { } //类A的构造函数,在初始化列表中对虚基类初始化
...
};
class B:virtual public F //F作为B的一个虚基类
{ B(int n): F(n){ } //类B的构造函数,在初始化列表中对虚基类初始化
...
};
class C:public A,public B
{C(int n):F(n),A(n),B(n) { } //类D的构造函数,在初始化列表中对所有基类完成初始化
...
};
1.上面的例子中可以看出,虚基类不是在声明基类时定义的,而是在声明派生类时,指定继承方式声明的。用virtual关键字进行声明,当基类通过多条派生路径被一个派生类继承时,该派生类只继承该基类一次,也就是说,基类成员只保留一次。
2.在定义C的构造函数时,与以往方法不同,以前派生类只需要对其直接基类进行初始化,再由直接基类对它自己的基类初始化,现在,由于虚基类在派生类中只有一份数据成员,所以这份数据成员的初始化必须交给派生类直接给出,故有以下结论:
在最后的派生类中,不仅要负责对其直接基类进行初始化,还要负责对虚基类初始化。
进阶:
但是不是意味着使用虚基类真的节省了空间了呢,请看下面的例子:
#include<iostream>
using namespace std;
class F
{
public :
F(int m){a=m;}
private:
int a;
};
class A:public F
{
public :
A(int m):F(m){}
};
class B:public F
{
public:
B(int m):F(m){}
};
class C:public A,public B
{
public :
C(int m,int n):A(m),B(n){}
};
void main()
{
C c1(1,2);
cout<<sizeof(c1)<<endl;
}
输出为8,因为调用了两次A的构造函数,所以两个int,占8字节。那么当使用虚基类时是不是相应的减少为4了呢?
#include<iostream>
using namespace std;
class F
{
public :
F(int m){a=m;}
private:
int a;
};
class A:virtual public F
{
public :
A(int m):F(m){}
};
class B:virtual public F
{
public:
B(int m):F(m){}
};
class C:public A,public B
{
public :
C(int m):F(m),A(m),B(m){}
};
void main()
{
C c1(1);
cout<<sizeof(c1)<<endl;
}
此时运行结果竟然不是4,而是12,这是为什么呢?
其实这设计到内存中的规则,当程序执行A的构造函数时发现F是虚基类,此时它在内存中为F开辟了一个空间,当它执行B的构造函数时发现F是虚基类,又在内存中为F开辟了一段空间,最后,当执行C的构造函数时,又在内存中为F开辟了一个空间,并把a的值存进去,同时让刚才开辟的两个空间都指向了最后开辟的空间,即前两个空间中只存放第三个空间的地址,这就解释了为什么会多出四个字节。