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

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

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

我们在基类中加上虚函数 我们再来看看类的内存分布:

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

到现在为止,你发现以上的类有什么问题吗? 没错,基类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;}

以下是上述类内存分布:

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

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

,我们首先去了象鼻山,那里景色秀丽神奇,

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

相关文章:

你感兴趣的文章:

标签云: