C++类内存分布+钻石模型的解决方法

来源:互联网 发布:怎样申请淘宝号 编辑:程序博客网 时间:2024/04/29 13:58

C++类内存分布

#include<iostream>using namespace std;class Base{    private:        int val;    public:        Base(int i = 0):val(i){cout<<val<<endl;}        ~Base(){}};class Derived1 : public Base{    private:        int a;    public:        Derived1(int i = 1):a(i){cout<<a<<endl;}            };class Derived2 : public Base{    private:        int b;    public:        Derived2(int i = 2):b(i){cout<<b<<endl;}};class Test : public Derived1, public Derived2{    private:        int c;    public:        Test(int i = 3):c(i){cout<<i<<endl;}};int main(){    cout<<sizeof(Base)<<endl;    cout<<sizeof(Derived1)<<endl;    cout<<sizeof(Derived2)<<endl;    cout<<sizeof(Test)<<endl;    return 0;}

以下为上述各类在内存中的分布情况:
类内存分布1
类内存分布2

我们在基类中加上虚函数 我们再来看看类的内存分布:
类内存分布3
类内存分布4
类内存分布5
由以上我们可以看出,类中多了一个指针成员
其实这个指针成员就是传说中的虚表指针
虚表是一个函数指针数组,它在编译程序时生成
虚表指针是一个函数指针数组的指针,在构造函数中,虚表指针被初始化指向虚表
在非虚继承的派生类中,派生类和基类公用一个虚表指针,这也是实现多态的基础,当我们用一个基类指针指向一个派生类对象,在调用函数时,我们会查阅虚表,进行相应的函数调用,从而实现多态.你可能会问那如果基类指针指向一个基类的对象,那么函数调用岂不是调用到了派生类的函数? 其实不是这样的,当基类指针指向一个基类对象时,它会直接调用基类的函数,而不会去查阅虚表. 需要指出的是查阅虚表需要进行解引用操作,故效率没有直接调用函数高,但是我们为了实现多态,付出这么一点代价也是值得的…

到现在为止,你发现以上的类有什么问题吗?
没错,基类Base的成员在Test类中存在两份,这就可能导致二义性的产生,那么如何解决这个问题呢?
答案是使用虚继承机制!
代码如下:

#include<iostream>using namespace std;class Base{    private:        int val;    public:        Base(int i = 0):val(i){cout<<val<<endl;}        virtual ~Base(){}};class Derived1 : virtual public Base{    private:        int a;    public:        Derived1(int i = 1):a(i){cout<<a<<endl;}            };class Derived2 : virtual public Base{    private:        int b;    public:        Derived2(int i = 2):b(i){cout<<b<<endl;}};class Test : public Derived1, public Derived2{    private:        int c;    public:        Test(int i = 3):c(i){cout<<i<<endl;}};int main(){    cout<<sizeof(Base)<<endl;    cout<<sizeof(Derived1)<<endl;    cout<<sizeof(Derived2)<<endl;    cout<<sizeof(Test)<<endl;    return 0;}

以下是上述类内存分布:
类内存分布6
类内存分布7
类内存分布8
我们从以上类的内存分布可以看出,在使用了虚继承机制后,基类成员在派生类中只有一份拷贝,解决了二义性的问题.但是同时,虚继承使得我们和基类不能共用同一个虚表指针,故增加了维护多个虚表的开销!

总结: 当基类中含有虚函数时
1.每个类都将含有虚表指针,虚表在编译期间生成,虚表指针在构造函数中进行初始化
2.当为非虚继承时,派生类和基类共享一个虚表指针
3.当为虚继承时,可以解决钻石模型带来的二义性的问题,但是同时派生类不能和基类共享虚表指针,每个派生类都有两个指针,一个指向基类的虚表,一个指向自己的虚表,这也增加了维护虚表的难度!

0 0
原创粉丝点击