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++中,多重继承是不推荐的,也并不常用,而一旦离开了多重继承,虚拟继承就完全失去了存在的必要因为这样只会降低效率和占用更多的空间。

为什么需要虚继承?

由于C++支持多重继承,那么在这种情况下会出现重复的基类这种情况,也就是说可能出现将一个类两次作为基类的可能性。比如像下面的情况

C++ 虚拟继承

 1 #include<iostream>
 2 using std::cout;
 3 using std::endl;
 4 class Base
 5 {
 6 protected:
 7 int value;
 8 public:
 9 Base()
10 {
11 cout<<"in Base"<<endl;
12 }
13 };
14 class DerivedA:protected Base
15 {
16 public:
17 DerivedA()
18 {
19 cout<<"in DerivedA"<<endl;
20 }
21 };
22 class DerivedB: protected Base
23 {
24 public:
25 DerivedB()
26 {
27 cout<<"in DerivedB"<<endl;
28 }
29 };
30 class MyClass:DerivedA,DerivedB
31 {
32 public:
33 MyClass()
34 {
35 cout<<"in MyClass"<<value<<endl;
36 }
37 };

 

编译时的错误如下

C++ 虚拟继承
这 中情况下会造成在MyClass中访问value时出现路径不明确的编译错误,要访问数据,就需要显示地加以限定。变成DerivedA::value或 者DerivedB::value,以消除歧义性。并且,通常情况下,像Base这样的公共基类不应该表示为两个分离的对象,而要解决这种问题就可以用虚 基类加以处理。如果使用虚继承,编译便正常了,类的结构示意图便如下。

C++ 虚拟继承

虚继承的特点是,在任何派生类中的virtual基类总用同一个(共享)对象表示,正是如上图所示。

 

 

2.引入虚继承和直接继承会有什么区别呢

由于有了间接性和共享性两个特征,所以决定了虚继承体系下的对象在访问时必然会在时间和空间上与一般情况有较大不同。

2.1时间:在通过继承类对象访问虚基类对象中的成员(包括数据成员和函数成员)时,都必须通过某种间接引用来完成,这样会增加引用寻址时间(就和虚函数一样),其实就是调整this指针以指向虚基类对象,只不过这个调整是运行时间接完成的。

2.2空间:由于共享所以不必要在对象内存中保存多份虚基类子对象的拷贝,这样较之 多继承节省空间。虚拟继承与普通继承不同的是,虚拟继承可以防止出现diamond继承时,一个派生类中同时出现了两个基类的子对象。也就是说,为了保证 这一点,在虚拟继承情况下,基类子对象的布局是不同于普通继承的。因此,它需要多出一个指向基类子对象的指针。

 

3.笔试,面试中常考的C++虚拟继承的知识点

第一种情况:         第二种情况:          第三种情况            第四种情况:
class a           class a              class a              class a
{              {                {                 {
    virtual void func();      virtual void func();       virtual void func();        virtual void func();
};              };                  char x;              char x;
class b:public virtual a   class b :public a           };                };
{              {                class b:public virtual a      class b:public a
    virtual void foo();        virtual void foo();     {                 {
};              };                  virtual void foo();        virtual void foo();
                               };                };

如果对这四种情况分别求sizeof(a),  sizeof(b)。结果是什么样的呢?下面是输出结果:(在vc6.0中运行)
第一种:4,12
第二种:4,4
第三种:8,16
第四种:8,8

详细分析可参考:http://blog.csdn.net/wangqiulin123456/article/details/8059536

想想这是为什么呢?

因为每个存在虚函数的类都要有一个4字节的指针指向自己的虚函数表,所以每种情况的类a所占的字节数应该是没有什么问题 的,那么类b的字节数怎么算呢?看“第一种”和“第三种”情况采用的是虚继承,那么这时候就要有这样的一个指针vptr_b_a,这个指针叫虚类指针,也 是四个字节;还要包括类a的字节数,所以类b的字节数就求出来了。而“第二种”和“第四种”情况则不包括vptr_b_a这个指针,这回应该木有问题了 吧。

C++ 虚拟继承
1 class a
2 {
3     virtual void func();
4 };
5 
6 class b:public a
7 {
8     void foo();
9 };
C++ 虚拟继承

此时:sizeof(a) = 4 , sizeof(b) = 4

1 class a
2 {
3     virtual void func();
4 };
5 
6 class b:public  a
7 {
8  virtual void foo();
9 };

奇怪的是,此时:sizeof(a) = 4 , sizeof(b) = 4。 尽管class b中在voif foo()前加了virtual,但结果却相同。

1 class a
2 {
3     virtual void func();
4 };
5 
6 class b:public virtual a
7 {
8     void foo();
9 };

此时:sizeof(a) = 4 , sizeof(b) = 8

C++ 虚拟继承
1 class a
2 {
3    void func();
4 };
5 
6 class b:public a
7 {
8     virtual void foo();
9 };
C++ 虚拟继承

此时:sizeof(a) = 1 , sizeof(b) = 4

C++ 虚拟继承
1 class a
2 {
3    void func();
4 };
5 
6 class b:public a
7 {
8     void foo();
9 };
C++ 虚拟继承

此时:sizeof(a) = 1 , sizeof(b) = 1

如下例:

C++ 虚拟继承
C++ 虚拟继承
 1 class A
 2 {
 3 };
 4 class A2
 5 {
 6 };
 7 class B : public A
 8 {
 9 };
10 class C : public virtual B
11 {
12 };
13 class D : public A , public A2
14 {
15 };
C++ 虚拟继承
C++ 虚拟继承

相关文章: