C++对象模型:单继承,多继承,虚继承

https://www.cnblogs.com/raichen/p/5744300.html

相关解释非常清晰,关于最后菱形虚继承可以参考此文

https://www.cnblogs.com/QG-whz/p/4909359.html

 

------待补充、有空会提炼精简相关内容------

一、类成员分类

 

C++对象模型:单继承,多继承,虚继承

c++的对象模型:(这也是正在使用的)

首先,非静态的数据成员放置在每一个类对象中,static数据成员放在类对象外。而函数,不管是静态还是非静态,都是在类外的,对于虚函数,通过虚函数表加上虚指针来支持。

  1. 每个生成一个虚表(virtual table,vtbl),虚表中存放一堆指针,指向该类的每一个虚函数,地址将按照声明顺序排列
  2. 每一个类对象,都拥有一个虚表指针(vptr),由编译器生成,虚表指针的设定和重置,都有类的复制控制(也就是构造,析构,赋值操作符)完成。现在许多编译器,把vptr放在一个类对象的最前端。

 

二、单继承(父类含虚函数)

原则:

  1. 子类和父类,都各自拥有一个虚函数表。
  2. 子类如果没有重写(override)父类的虚函数,则,直接用父类的虚函数。
  3. 如果子类重写了父类虚函数,则子类虚函数会覆盖虚表中对应的父类虚函数
  4. 如果子类声明了自己新的虚函数,该虚函数地址会扩充到虚函数表最后。
#include <iostream>
using namespace std;

class Base
{
public:
    virtual void fun1(){ cout << "Base fun1" << endl; }
    virtual void fun2(){ cout << "Base fun2" << endl; }
private:
    int a;
};

class Derive :  public Base
{
public:
    void fun2(){ cout << "Derive fun2" << endl; }
    virtual void fun3(){}
private:
    int b;
};

int main()
{
    Base b;
    Derive d;
    Base *p = &d;
    p->fun1();
    p->fun2();

    system("pause");
    return 0;
}

 

C++对象模型:单继承,多继承,虚继承

 

 

三、多继承

 (1)一般多继承(非菱形继承)

  1.  如果子类新增虚函数,放在声明的第一个父类虚函数表中。
  2. 如果子类重写了父类的虚函数,所有父类的虚函数表都会改变。
  3. 内存布局中,父类按照声明顺序排列。
#include <iostream>
using namespace std;

class Base1
{
public:
    virtual void fun1(){}
private:
    int m_base1;
};

class Base2
{
public:
    virtual void fun1(){}
    virtual void fun2(){}
private:
    int m_base2;
};

class Derive :  public Base1,public Base2
{
public:
    void fun1(){}
    virtual void fun3(){}
private:
    int m_derive;
};

int main()
{
    Base1 b1;
    Base2 b2;
    Derive d;

    cout <<"b1:" <<sizeof(b1) << endl;
    cout << "b2:" << sizeof(b2) << endl;
    cout <<"d:" << sizeof(d) << endl;
    system("pause");
    return 0;
}

 

 C++对象模型:单继承,多继承,虚继承

 

 (2)菱形继承

    也叫重复继承,基类被某个派生类简单重复继承了多次。在派生类中拥有多份基类实例。

C++对象模型:单继承,多继承,虚继承

 

class B

{

public:

    int ib;

public:

    B(int i=1) :ib(i){}

    virtual void f() { cout << "B::f()" << endl; }

    virtual void Bf() { cout << "B::Bf()" << endl; }

};

class B1 : public B

{

public:

    int ib1;

public:

    B1(int i = 100 ) :ib1(i) {}

    virtual void f() { cout << "B1::f()" << endl; }

    virtual void f1() { cout << "B1::f1()" << endl; }

    virtual void Bf1() { cout << "B1::Bf1()" << endl; }



};

class B2 : public B

{

public:

    int ib2;

public:

    B2(int i = 1000) :ib2(i) {}

    virtual void f() { cout << "B2::f()" << endl; }

    virtual void f2() { cout << "B2::f2()" << endl; }

    virtual void Bf2() { cout << "B2::Bf2()" << endl; }

};


class D : public B1, public B2

{

public:

    int id;



public:

    D(int i= 10000) :id(i){}

    virtual void f() { cout << "D::f()" << endl; }

    virtual void f1() { cout << "D::f1()" << endl; }

    virtual void f2() { cout << "D::f2()" << endl; }

    virtual void Df() { cout << "D::Df()" << endl; }

};

 

C++对象模型:单继承,多继承,虚继承

 

 我们可以看到在D类中,有俩个B类的数据成员,一个来自B1,一个来自B2,会引起程序歧义。

调用看看。

C++对象模型:单继承,多继承,虚继承C++对象模型:单继承,多继承,虚继承

 

  

三、虚继承

虚继承可以解决菱形继承中拥有多个间接父类实例的情况。

(1)简单虚继承

  1. 虚继承的子类,如果本身定义了新的虚函数,则编译器为其生存一个新的函数指针(vptr)以及一张新的虚函数表。这个vptr位于对象内存最前面。
  2. 虚继承的子类中,单独保留了父类的vptr与虚函数表
  3. 虚继承的子类中,还有虚基类表指针(vbptr)

当虚继承时,子类会生成一个隐藏的虚基类指针(当然也占用内存) vbptr,这个虚基类表指针总在虚函数指针后。

因而,对某个类实例来说,如果它有虚基类指针,那么虚基类指针可能在实例的0字节偏移处(该类没有vptr时,vbptr就处于类实例内存布局的最前面,否则vptr处于类实例内存布局的最前面),也可能在类实例的4字节偏移处。

 C++对象模型:单继承,多继承,虚继承

 

 

 C++对象模型:单继承,多继承,虚继承

 

 C++对象模型:单继承,多继承,虚继承

 

 

int main()
{
B1 a;
    cout <<"B1对象内存大小为:"<< sizeof(a) << endl;

    //取得B1的虚函数表
    cout << "[0]B1::vptr";
    cout << "t地址:" << (int *)(&a)<< endl;

    //输出虚表B1::vptr中的函数
    for (int i = 0; i<2;++ i)
    {
        cout << "  [" << i << "]";
        Fun fun1 = (Fun)*((int *)*(int *)(&a) + i);
        fun1();
        cout << "t地址:t" << *((int *)*(int *)(&a) + i) << endl;
    }

    //[1]
    cout << "[1]vbptr "  ;
    cout<<"t地址:" << (int *)(&a) + 1<<endl;  //虚表指针的地址
    //输出虚基类指针条目所指的内容
    for (int i = 0; i < 2; i++)
    {
        cout << "  [" << i << "]";

        cout << *(int *)((int *)*((int *)(&a) + 1) + i);

        cout << endl;
    }


    //[2]
    cout << "[2]B1::ib1=" << *(int*)((int *)(&a) + 2);
    cout << "t地址:" << (int *)(&a) + 2;
    cout << endl;

    //[3]
    cout << "[3]值=" << *(int*)((int *)(&a) + 3);
    cout << "tt地址:" << (int *)(&a) + 3;
    cout << endl;

    //[4]
    cout << "[4]B::vptr";
    cout << "t地址:" << (int *)(&a) +3<< endl;

    //输出B::vptr中的虚函数
    for (int i = 0; i<2; ++i)
    {
        cout << "  [" << i << "]";
        Fun fun1 = (Fun)*((int *)*((int *)(&a) + 4) + i);
        fun1();
        cout << "t地址:t" << *((int *)*((int *)(&a) + 4) + i) << endl;
    }

    //[5]
    cout << "[5]B::ib=" << *(int*)((int *)(&a) + 5);
    cout << "t地址: " << (int *)(&a) + 5;
    cout << endl;

C++对象模型:单继承,多继承,虚继承

 

 

(2)虚拟菱形继承(菱形虚继承)

  1. 在D类对象内存中,基类出现的顺序是:先是B1(最左父类),然后是B2(次左父类),最后是B(虚祖父类)
  2. D类对象的数据成员id放在B类前面,两部分数据依旧以0来分隔。
  3. 编译器没有为D类生成一个它自己的vptr,而是覆盖并扩展了最左父类的虚基类表,与简单继承的对象模型相同。
  4. 超类B的内容放到了D类对象内存布局的最后。

C++对象模型:单继承,多继承,虚继承

 

 C++对象模型:单继承,多继承,虚继承

 

int main()
{
    D d;
    cout << "D对象内存大小为:" << sizeof(d) << endl;

    //取得B1的虚函数表
    cout << "[0]B1::vptr";
    cout << "t地址:" << (int *)(&d) << endl;

    //输出虚表B1::vptr中的函数
    for (int i = 0; i<3; ++i)
    {
        cout << "  [" << i << "]";
        Fun fun1 = (Fun)*((int *)*(int *)(&d) + i);
        fun1();
        cout << "t地址:t" << *((int *)*(int *)(&d) + i) << endl;
    }

    //[1]
    cout << "[1]B1::vbptr ";
    cout << "t地址:" << (int *)(&d) + 1 << endl;  //虚表指针的地址
    //输出虚基类指针条目所指的内容
    for (int i = 0; i < 2; i++)
    {
        cout << "  [" << i << "]";

        cout << *(int *)((int *)*((int *)(&d) + 1) + i);

        cout << endl;
    }


    //[2]
    cout << "[2]B1::ib1=" << *(int*)((int *)(&d) + 2);
    cout << "t地址:" << (int *)(&d) + 2;
    cout << endl;

    //[3]
    cout << "[3]B2::vptr";
    cout << "t地址:" << (int *)(&d) + 3 << endl;

    //输出B2::vptr中的虚函数
    for (int i = 0; i<2; ++i)
    {
        cout << "  [" << i << "]";
        Fun fun1 = (Fun)*((int *)*((int *)(&d) + 3) + i);
        fun1();
        cout << "t地址:t" << *((int *)*((int *)(&d) + 3) + i) << endl;
    }

    //[4]
    cout << "[4]B2::vbptr ";
    cout << "t地址:" << (int *)(&d) + 4 << endl;  //虚表指针的地址
    //输出虚基类指针条目所指的内容
    for (int i = 0; i < 2; i++)
    {
        cout << "  [" << i << "]";

        cout << *(int *)((int *)*((int *)(&d) + 4) + i);

        cout << endl;
    }

    //[5]
    cout << "[5]B2::ib2=" << *(int*)((int *)(&d) + 5);
    cout << "t地址: " << (int *)(&d) + 5;
    cout << endl;

    //[6]
    cout << "[6]D::id=" << *(int*)((int *)(&d) + 6);
    cout << "t地址: " << (int *)(&d) + 6;
    cout << endl;

    //[7]
    cout << "[7]值=" << *(int*)((int *)(&d) + 7);
    cout << "tt地址:" << (int *)(&d) + 7;
    cout << endl;

    //间接父类
    //[8]
    cout << "[8]B::vptr";
    cout << "t地址:" << (int *)(&d) + 8 << endl;

    //输出B::vptr中的虚函数
    for (int i = 0; i<2; ++i)
    {
        cout << "  [" << i << "]";
        Fun fun1 = (Fun)*((int *)*((int *)(&d) + 8) + i);
        fun1();
        cout << "t地址:t" << *((int *)*((int *)(&d) + 8) + i) << endl;
    }

    //[9]
    cout << "[9]B::id=" << *(int*)((int *)(&d) + 9);
    cout << "t地址: " << (int *)(&d) +9;
    cout << endl;

    getchar();
}

 

 C++对象模型:单继承,多继承,虚继承

 

原文链接: https://www.cnblogs.com/EvansPudding/p/12565699.html

欢迎关注

微信关注下方公众号,第一时间获取干货硬货;公众号内回复【pdf】免费获取数百本计算机经典书籍;

也有高质量的技术群,里面有嵌入式、搜广推等BAT大佬

    C++对象模型:单继承,多继承,虚继承

原创文章受到原创版权保护。转载请注明出处:https://www.ccppcoding.com/archives/337644

非原创文章文中已经注明原地址,如有侵权,联系删除

关注公众号【高性能架构探索】,第一时间获取最新文章

转载文章受原作者版权保护。转载请注明原作者出处!

(0)
上一篇 2023年3月1日 下午11:14
下一篇 2023年3月1日 下午11:14

相关推荐