在 C++ 中,在使用 多继承 时,如果发生了 菱形继承,那么就会出现数据冗余的问题,为了解决菱形继承出现的数据冗余的问题,C++ 提出了虚继承,虚继承使得派生类中只保留一份间接基类的成员。
虚继承的目的是让某个类做出声明,承诺愿意共享它的基类。其中,这个被共享的基类就称为虚基类(Virtual Base Class),菱形继承中的顶层类 就是一个虚基类。在这种机制下,不论虚基类在继承体系中出现了多少次,在派生类中都只包含一份虚基类的成员。
虚继承关系,如下图所示:
观察这个新的继承体系,我们会发现虚继承的一个不太直观的特征:必须在虚派生的真实需求出现前就已经完成虚派生的操作。在上图中,当定义 D 类时才出现了对虚派生的需求,但是如果 B 类和 C 类不是从 A 类虚派生得到的,那么 D 类还是会保留 A 类的两份成员。
换个角度讲,虚派生只影响从指定了虚基类的派生类中进一步派生出来的类,它不会影响派生类本身。
在实际开发中,位于中间层次的基类将其继承声明为虚继承一般不会带来什么问题。通常情况下,使用虚继承的类层次是由一个人或者一个项目组一次性设计完成的。对于一个独立开发的类来说,很少需要基类中的某一个类是虚基类,况且新类的开发者也无法改变已经存在的类体系。
C++ 标准库中的 iostream 类就是一个虚继承的实际应用案例。iostream 从 istream 和 ostream 直接继承而来,而 istream 和 ostream 又都继承自一个共同的名为 base_ios 的类,是典型的菱形继承。此时 istream 和 ostream 必须采用虚继承,否则将导致 iostream 类中保留两份 base_ios 类的成员。
iostream 继承体系如下图:
class B: virtual public A{ //虚继承
protected:
int m_b;
};
我们使用了类 B 虚继承自了类 A。
使用虚继承,解决菱形继承命名冲突问题
#include <iostream>
using namespace std;
// 间接基类A
class A
{
protected:
int m_a;
};
// 直接基类B
class B: public A
{
protected:
int m_b;
};
// 直接基类C
class C: public A
{
protected:
int m_c;
};
//派生类D
class D: public B, public C
{
public:
void seta(int a)
{
m_a = a; //命名冲突
}
void setb(int b)
{
m_b = b; //正确
}
void setc(int c)
{
m_c = c; //正确
}
void setd(int d)
{
m_d = d; //正确
}
private:
int m_d;
};
int main()
{
cout << "嗨客网(www.haicoder.net)\n" << endl;
D d;
return 0;
}
程序运行后,控制台输出如下:
我们在子类 D 中,直接访问了从类 A 中继承来的成员变量 m_a,此时程序报错,因为,这时候的菱形继承,此时,类 D 中有两份成员变量 m_a,一份是来自从 A->B->D 的路径,一份是来自从 A->C->D 的路径。
因此,此时程序报错,现在,我们修改程序,使用虚继承,代码如下:
#include <iostream>
using namespace std;
// 间接基类A
class A
{
protected:
int m_a;
};
// 直接基类B
class B: virtual public A
{
protected:
int m_b;
};
// 直接基类C
class C: virtual public A
{
protected:
int m_c;
};
//派生类D
class D: public B, public C
{
public:
void seta(int a)
{
m_a = a; //命名冲突
}
void setb(int b)
{
m_b = b; //正确
}
void setc(int c)
{
m_c = c; //正确
}
void setd(int d)
{
m_d = d; //正确
}
private:
int m_d;
};
int main()
{
cout << "嗨客网(www.haicoder.net)\n" << endl;
D d;
return 0;
}
程序运行后,控制台输出如下:
这次,我们将类 A 继承类 B 使用了虚继承,类 B 继承类 C 也使用了虚继承,这样在派生类 D 中就只保留了一份成员变量 m_a,因此,程序不再报错。
在 C++ 中,在使用多继承时,如果发生了菱形继承,那么就会出现数据冗余的问题,为了解决菱形继承出现的数据冗余的问题,C++ 提出了虚继承,虚继承使得派生类中只保留一份间接基类的成员。
虚继承的目的是让某个类做出声明,承诺愿意共享它的基类。其中,这个被共享的基类就称为虚基类(Virtual Base Class),菱形继承中的顶层类 就是一个虚基类。在这种机制下,不论虚基类在继承体系中出现了多少次,在派生类中都只包含一份虚基类的成员。