C++之虚拟继承和虚基类

来源:互联网 发布:隐形眼镜 知乎 编辑:程序博客网 时间:2024/06/05 05:34

        昨天的两篇文章《C++之类的继承》和《C++之虚函数》带领我们认识了C++中的类的继承方法,以及多态的实现。今天我们来学习一下二者的结合体——虚拟继承和虚基类。

        在讲解什么是虚拟继承之前,我们需要先普及一下子类和父类在内存中的关系。如图1所示,每一个子类在内存中都拥有父类内存的一份拷贝。

父类与子类在内存中的关系1

图1 子类和父类在内存中的关系

子类对象占用的内存始终会比父类对象占用的内存大,因为子类继承父类,也就继承了父类的方法接口,所以在子类对象中有一份父类的内存拷贝。

        接下来说说单一继承菱形继承

1)单一继承是指每一个子类都只有一个父类,如图2所示。单一继承的内存关系如图3所示。

 

父类与子类在内存中的关系2                                               父类与子类在内存中的关系3

图2 单一继承                                                               图3 单一继承的内存关系           

2)菱形继承是指一个类的两个父类有共同的父类。如图4所示。菱形继承的内存关系如图5所示。

菱形继承                   菱形继承的内存关系

图4 菱形继承                                                                                  图5 菱形继承的内存关系                                

        当我们看到菱形继承的时候,是不是发现一点问题。第一,类C中包含类A的两份一模一样的拷贝。第二,类A的构造函数被重复调用了两次。如果读者不相信,可以试一试下面的代码。

#include <iostream>   
using namespace std;   
class A  
{  
public:  
    A(){cout<<"This is A constructor."<<endl;}  
    ~A(){cout<<"This is A destructor."<<endl;}  
    void speak(){cout<<"This is A speak!"<<endl;} 
};  
  
class B1: public A 
{  
 public:  
     B1(){cout<<"This is B1 constructor."<<endl;}  
    ~B1(){cout<<"This is B1 destructor."<<endl;}  
};  
class B2: public A 
{  
public:  
    B2(){cout<<"This is B2 constructor."<<endl;}  
    ~B2(){cout<<"This is B2 destructor."<<endl;}  
};  
  
class C:public B1,public B2  
{  
public:  
    C(){cout<<"This is C constructor."<<endl;}  
    ~C(){cout<<"This is C destructor."<<endl;}  
}; 
int main()   
{   
    C *c=new C(); 
    //c->speak(); 
    delete c; 
    return 0;   
}
程序的运行结果如下:

2011-07-02_214701

如果我们在代码中再添加如下代码:“c->speak();”,编译器还会报出错误。错误信息如下:

Error    1    error C2385: ambiguous access of 'speak'    e:\XXX\main.cpp    38
这是由于类B1和类B2都有函数speak(),编译器不知道应该执行哪一个,此处产生了二义性。

        综上所述,菱形继承主要是一下三个问题:

    • 存储问题:占用了更多的存储空间。
    • 效率问题:基类的构造(析构)函数被调用了多次。
    • 编译问题:调用基类的成员函数会产生二义性。

        现在,终于轮到虚拟继承出场了!虚拟继承其实很简单,就是在基类前面加上一个virtual关键字。修改之后的代码如下:

#include <iostream>   
using namespace std;   
class A  
{  
public:  
    A(){cout<<"This is A constructor."<<endl;}  
    ~A(){cout<<"This is A destructor."<<endl;}  
    void speak(){cout<<"This is A speak!"<<endl;} 
};  
  
class B1: public virtual A 
{  
 public:  
     B1(){cout<<"This is B1 constructor."<<endl;}  
    ~B1(){cout<<"This is B1 destructor."<<endl;}  
};  
class B2: virtual public A 
{  
public:  
    B2(){cout<<"This is B2 constructor."<<endl;}  
    ~B2(){cout<<"This is B2 destructor."<<endl;}  
};  
  
class C:public B1,public B2  
{  
public:  
    C(){cout<<"This is C constructor."<<endl;}  
    ~C(){cout<<"This is C destructor."<<endl;}  
}; 
int main()   
{   
    C *c=new C(); 
    c->speak(); 
    delete c; 
    return 0;   
}

注意关键字virtual的位置。这时我们再运行程序,得到的结果如下:

2011-07-02_220338

看到了吧,class A的构造函数只执行了一遍,而且speak()函数正常执行,没有产生二义性。忘了说一句,被虚拟继承的基类就是虚基类,例如上面程序中的class A。

        今天虚拟继承和虚基类就总结到这,如有疑问,欢迎评论!

感谢:《C++编程关键路径——程序员求职指南》

0 0
原创粉丝点击