C++虚继承

C++虚继承教程

C++ 中,在使用 多继承 时,如果发生了 菱形继承,那么就会出现数据冗余的问题,为了解决菱形继承出现的数据冗余的问题,C++ 提出了虚继承,虚继承使得派生类中只保留一份间接基类的成员。

为什么要虚继承

虚继承的目的是让某个类做出声明,承诺愿意共享它的基类。其中,这个被共享的基类就称为虚基类(Virtual Base Class),菱形继承中的顶层类 就是一个虚基类。在这种机制下,不论虚基类在继承体系中出现了多少次,在派生类中都只包含一份虚基类的成员。

虚继承关系,如下图所示:

24_C虚继承.png

观察这个新的继承体系,我们会发现虚继承的一个不太直观的特征:必须在虚派生的真实需求出现前就已经完成虚派生的操作。在上图中,当定义 D 类时才出现了对虚派生的需求,但是如果 B 类和 C 类不是从 A 类虚派生得到的,那么 D 类还是会保留 A 类的两份成员。

换个角度讲,虚派生只影响从指定了虚基类的派生类中进一步派生出来的类,它不会影响派生类本身。

在实际开发中,位于中间层次的基类将其继承声明为虚继承一般不会带来什么问题。通常情况下,使用虚继承的类层次是由一个人或者一个项目组一次性设计完成的。对于一个独立开发的类来说,很少需要基类中的某一个类是虚基类,况且新类的开发者也无法改变已经存在的类体系。

C++ 标准库中的 iostream 类就是一个虚继承的实际应用案例。iostream 从 istream 和 ostream 直接继承而来,而 istream 和 ostream 又都继承自一个共同的名为 base_ios 的类,是典型的菱形继承。此时 istream 和 ostream 必须采用虚继承,否则将导致 iostream 类中保留两份 base_ios 类的成员。

iostream 继承体系如下图:

25_C虚继承.png

C++虚继承详解

语法

class B: virtual public A{ //虚继承 protected: int m_b; };

说明

我们使用了类 B 虚继承自了类 A。

案例

C++虚继承

使用虚继承,解决菱形继承命名冲突问题

#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; }

程序运行后,控制台输出如下:

26_C虚继承.png

我们在子类 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; }

程序运行后,控制台输出如下:

27_C虚继承.png

这次,我们将类 A 继承类 B 使用了虚继承,类 B 继承类 C 也使用了虚继承,这样在派生类 D 中就只保留了一份成员变量 m_a,因此,程序不再报错。

C++虚继承总结

在 C++ 中,在使用多继承时,如果发生了菱形继承,那么就会出现数据冗余的问题,为了解决菱形继承出现的数据冗余的问题,C++ 提出了虚继承,虚继承使得派生类中只保留一份间接基类的成员。

虚继承的目的是让某个类做出声明,承诺愿意共享它的基类。其中,这个被共享的基类就称为虚基类(Virtual Base Class),菱形继承中的顶层类 就是一个虚基类。在这种机制下,不论虚基类在继承体系中出现了多少次,在派生类中都只包含一份虚基类的成员。