应用范例

  1. // 来自openVPN rc.hpp
  2. // 当对象继承使用“引用计数“的类时,需要使用”虚“继承
  3. // Note that RC/RCWeak fully supports virtual inheritance. For
  4. // example, consider the diamond inheritance pattern below, where
  5. // both A and B objects contain their own reference count, but C
  6. // inherits from both A and B. To prevent C objects from
  7. // having two separate reference counts, it is necessary to
  8. // virtually inherit from RC.
  9. //
  10. // class A : public virtual RC<thread_unsafe_refcount> {}
  11. // class B : public virtual RC<thread_unsafe_refcount> {}
  12. // class C : public A, public B {}

了解Diamond Inheritance (非虚基类)

  1. class A {
  2. public:
  3. int a;
  4. };
  5. class B : public A {
  6. };
  7. class C : public A {
  8. };
  9. class D : public B, public C {
  10. };
  11. int main() {
  12. D d;
  13. d.a = 1;
  14. return 0;
  15. }

这个代码会报错,因为d中保存了两份A的副本,即有两个成员变量a,一般不会报错,但是一旦对D中的a使用,就会报一个“对a的访问不明确”。虚继承就可以解决这个问题。在探讨虚函数之前,先来一个sizeof的问题。

不明确的a,即编译器无法分清到底a是继承自B的还是继承自C的。
解决上面由于菱形继承而产生二义性与数据冗余的问题,需要用到虚继承。
虚继承的提出就是为了解决多重继承时,可能会保存两份副本的问题,也就是说用了虚继承就只保留了一份副本,但是这个副本是被多重继承的基类所共享的,该怎么实现这个机制呢?

如果基类中没有成员变量, 使用虚继承

  1. #include <iostream>
  2. using namespace std;
  3. class B //基类
  4. {
  5. public:
  6. B()
  7. {
  8. cout << "B" << endl;
  9. }
  10. ~B()
  11. {
  12. cout << "~B()" << endl;
  13. }
  14. };
  15. class C1 :virtual public B
  16. {
  17. public:
  18. C1()
  19. {
  20. cout << "C1()" << endl;
  21. }
  22. ~C1()
  23. {
  24. cout << "~C1()" << endl;
  25. }
  26. };
  27. class C2 :virtual public B
  28. {
  29. public:
  30. C2()
  31. {
  32. cout << "C2()" << endl;
  33. }
  34. ~C2()
  35. {
  36. cout << "~C2()" << endl;
  37. }
  38. };
  39. class D :public C1, public C2
  40. {
  41. public:
  42. D()
  43. {
  44. cout << "D()" << endl;
  45. }
  46. ~D()
  47. {
  48. cout << "~D()" << endl;
  49. }
  50. };
  51. int main()
  52. {
  53. cout << sizeof(B) << endl;
  54. cout << sizeof(C1) << endl;
  55. cout << sizeof(C2) << endl;
  56. cout << sizeof(D) << endl;
  57. return 0;
  58. }

输出:
image.png

总结

1.为什么要引入虚拟继承
虚拟继承是多重继承中特有的概念。虚拟基类是为解决多重继承而出现的。如:类D继承自类B1、B2,而类B1、B2都继承自类A,因此在类D中两次出现类A中的变量和函数。为了节省内存空间,可以将B1、B2对A的继承定义为虚拟继承,而A就成了虚拟基类。实现的代码如下:
class A
class B1:public virtual A;
class B2:public virtual A;
class D:public B1,public B2;
虚拟继承在一般的应用中很少用到,所以也往往被忽视,这也主要是因为在C++中,多重继承是不推荐的,也并不常用,而一旦离开了多重继承,虚拟继承就完全失去了存在的必要因为这样只会降低效率和占用更多的空间。

2.引入虚继承和直接继承会有什么区别呢
由于有了间接性和共享性两个特征,所以决定了虚继承体系下的对象在访问时必然会在时间和空间上与一般情况有较大不同。
2.1时间:在通过继承类对象访问虚基类对象中的成员(包括数据成员和函数成员)时,都必须通过某种间接引用来完成,这样会增加引用寻址时间(就和虚函数一样),其实就是调整this指针以指向虚基类对象,只不过这个调整是运行时间接完成的。
2.2空间:由于共享所以不必要在对象内存中保存多份虚基类子对象的拷贝,这样较之多继承节省空间。虚拟继承与普通继承不同的是,虚拟继承可以防止出现diamond继承时,一个派生类中同时出现了两个基类的子对象。也就是说,为了保证这一点,在虚拟继承情况下,基类子对象的布局是不同于普通继承的。因此,它需要多出一个指向基类子对象的指针。

3.笔试,面试中常考的C++虚拟继承的知识点
Screen Shot 2019-11-28 at 7.01.03 PM.png

引用

https://www.cnblogs.com/xien7/archive/2013/03/12/2954364.html
https://blog.csdn.net/lixungogogo/article/details/51123182
https://www.cnblogs.com/BeyondAnyTime/archive/2012/06/05/2537451.html