菱形的虚拟继承

问题引入:

如果有Base类,B1类,B2类,D类,如下图继承关系

那么按一般的继承来看,D类创造的对象会继承B1类的方法与成员,同时也会继承B2类的方法与成员;

接下来类B1 , B2 会分别去继承Base类的方法与成员,那么D类的对象在调用Base类的方法时,到底是继承B1类这边的Base,还是会继承B2类这边的Base,此时就会产生二义性

为了解决这个问题,就看看虚拟继承是怎么来解决这个二义性问题的

  1. 菱形的虚拟继承的源代码

#include <iostream>

using namespace std;

class Base

{

public:

Base()

{

cout << "Base()  " << this << endl;

}

~Base()

{

cout << "~Base()  " << this << endl;

}

int m_data1;

};

class B1 : virtual public Base

{

public:

B1()

{

cout << "B1()  " << this << endl;

}

~B1()

{

cout << "~B1()  " << this << endl;

}

int m_data2;

};

class B2 : virtual public Base

{

public:

B2()

{

cout << "B2()  " << this << endl;

}

~B2()

{

cout << "~B2()  " << this << endl;

}

int m_data3;

};

class D : public B1, public B2

{

public:

D()

{

B1::m_data2 = 0x1;

B1::m_data1 = 0x2;

B2::m_data3 = 0x3;

B2::m_data1 = 0x4;

m_data4 = 0x5;

cout << "D()" << this << endl;

}

~D()

{

cout << "~D()  " << this << endl;

}

int m_data4;

};

void Test()

{

D d;

//cout << sizeof(D) << endl;   //24

}

int main()

{

Test();

getchar();

return 0;

}

2.断点打到D类的构造函数这条语句B1::m_data2 = 0x1;运行完D类构造函数后调试。

内存1中的地址栏输入&d可以看到类D对象的模型(取&d因为在Test()函数创建的是D类的d对象)

3.接下来问题就来了,明明是取D类对象d的地址,为什么这个地址下有存储了地址,里面到底是什么东  西,那就看看吧,(可以看看反汇编和相应的内存)

此处可以看到它的地址偏移4个字节中存储了0X 00 00 00 14

(这个数据有什么意义哪?)

在看看反汇编就可以了解到编译器到底在搞什么鬼

看看在给m_data1赋值时,也就是给类Base数据赋值时,是什么情况

—————————————————————————————————

第一步 取得this指针给寄存器eax

第二步 将寄存器eax中内容(即地址 (0x 00 B7 DC C8))给 ecx

第三步 将寄存器exc中内容加4 (即 地址(0x 00 B7 DC CC))后,取其地址内容给edx(内容为 0X 00 00 00 14)

第四步 取this指针给寄存器eax

第五步 eax + edx 的结果为 (0x 00 30 FD 60 +  0X 00 00 00 14 = 0x 00 30 FD 74),接下来将数据2

存放到这个地址下

——————————————————————————————————————

从模型图可以看到地址(0x 00 30 FD 74)就是Base类的地址

为什么存放的是数据2,但是结果是4,这就要看清楚了,下面还有条语句未执行,

那就是  B2::m_data1 = 0x4;

这就是菱形虚拟继承下为了不产生二义性的奥妙

(D类继承Base类的方法与成员,编译器在内存中只开辟了一块)

时间: 2024-10-24 14:11:19

菱形的虚拟继承的相关文章

含有虚函数菱形的虚拟继承(没有对虚函数进行重写)

在VS2013编程,调试 问题 :  菱形继承会引来,二义性 1.源代码 </pre><pre name="code" class="cpp">#include <iostream> using namespace std; class Base { public: virtual void FunTest() { cout << "Base::FunTest () " << endl;

菱形虚拟继承&虚函数表&对象模型

菱形继承: Assitant的菱形继承对象模型 Assitant中有两份Person成员,存在二义性和数据冗余. 所以我们引出了虚拟继承. virtual虚继.不会在子类中创建父类成员,但是子类中可以引用,就像指针一样.主要用在菱形继承,也叫钻石继承. 虚拟继承对象模型 class Student : vitrual public Person class Teacher : virtual public Peraon 虚函数表 通过一块连续内存来存储虚函数的地址.这张表解决了继承.虚函数(重写

菱形虚拟继承

最近在学习C++的继承,菱形继承是比较难懂的一部分,所以通过查阅资料后整理了一下思路,也和同学分享了关于菱形虚拟继承的一些东西. 菱形继承在一般情况下使用不到的,但是通过了解菱形继承,我们 可以了解编译器是如何工作的. 首先,菱形虚拟继承就是两个基类虚拟继承一个共同的超类,然后两个基类由有一个共同的派生类. class Base { public:     void func()     {         cout << "Base::func"<<endl;

菱形虚拟虚拟继承模型探索

ezeasasclass Person { public:                   virtual void fun6()                 {                                 cout << "Person::fun6()" << endl;                 } public:                  int  _name;                  int _majo

C++ 继承、多继承、虚拟继承对象模型

C++面向对象语言一大难点是继承,但又是不得不掌握的.简单的继承是很容易理解的,但是当涉及到多继承,设计到虚函数的继承,特别是涉及到虚继承时,问题就会变得复杂.下面的内容来自参考资料中的三篇文章.C++的继承学习中,最主要是要掌握派生类的对象模型,基类和派生类指针之间的向上向下类型转换,当继承中的出现虚函数成员函数的访问(多态),虚继承是如何通过引入虚基表解决"菱形继承"中存在多份公共基类的问题. 一.简单的对象模型 1.定义 class MyClass { public: int v

C++虚拟继承 类的内存布局

1. 单个虚拟继承 只是为了分析而已,实际中并没有太大的作用.跟虚拟继承相关的派生类对象的内存布局跟具体的编译器相关. (1)VS编译器:无论有无虚函数,必然含有虚基类表指针.虚基类表中的内容为本类实例的偏移和基类实例的相对偏移值.如果有虚函数,那么基类的虚函数表跟派生类的虚函数表是分开的. 在内存布局上,地址从低到高,顺序如下:派生类的虚函数表指针+虚基类表指针+派生类的成员变量+"间隔"(4个字节)+基类的虚函数表指针+基类的成员变量.派生类跟基类实例的位置关系跟普通继承正好相反.

虚拟继承的类对象的空间大小

// 含有虚拟继承对象的空间大小.cpp : 定义控制台应用程序的入口点. // #include "stdafx.h" #include <iostream> using namespace std; class A{ public: protected: private: }; class B{ public: protected: private: }; class C:public A,public B{ public: protected: private: };

虚函数和虚拟继承的内存分布

一.虚函数 (1)C++中的虚函数的主要作用:实现了多态的机制. (2)多态:用父类型的指针指向其子类的实例,然后通过父类的指针调用实际子类的成员函数.这种技术可以让父类的指针有"多种形态",这是一种泛型技术.所谓泛型技术,说白了就是试图使用不变的代码来实现可变的算法.比如:模板技术,RTTI技术,虚函数技术,要么是试图做到在编译时决议,要么试图做到运行时决议. (3)多态要基于函数重载,所以如果子类没有重载父类的虚函数那是一件毫无意义的事情. 二.虚函数表 1.虚函数表:虚函数表C+

c++,为什么要引入虚拟继承

虚拟基类是为解决多重继承而出现的. 以下面的一个例子为例: #include <iostream.h> #include <memory.h> class CA { int k; //如果基类没有数据成员,则在这里多重继承编译不会出现二义性 public: void f() {cout << "CA::f" << endl;} }; class CB : public CA { }; class CC : public CA { }; c