zoukankan      html  css  js  c++  java
  • C++对象模型:单继承,多继承,虚继承,菱形虚继承,及其内存布局图

    C++目前使用的对象模型:

    此模型下,nonstatic数据成员被置于每一个类的对象中,而static数据成员则被置于类对象之外static和nonstatic函数也都放在类对象之外(通过函数指针指向),而对于virtual函数,则通过虚函数表+虚函数指针来支持

    1)每个类生成一个表格,称为虚表(virtual table,简称vtbl),虚函数表中存在一堆指针,这些指针指向该类的每一个虚函数,虚表中的函数地址按照声明时的顺序排列

    2)每个类对象都有一个虚表指针(简称vptr),由编译器为其生成,虚表指针的设定和重置皆由类的相关函数控制(构造函数,析构函数,赋值操作符),虚表指针vptr的位置由编译器决定,一般编译器把vptr放在一个类对象的最前端(也就是说对象的地址就是vptr的地址)

    3)虚函数表的前面设置了一个指向type-info的指针,用以支持RTTI(Run Time Type Identification,运行时类型识别),RTTI是为支持多态生成的信息,包括对象的继承关系,对象本身的描述等,只有具有虚函数的对象才会生成

    样例:

    class Base
    {
    public:
     
        Base(int i) :baseI(i){};
      
        int getI(){ return baseI; }
     
        static void countI(){};
     
        virtual void print(void){ cout << "Base::print()"; }
    
        virtual ~Base(){}
     
    private:
     
        int baseI;
     
        static int baseS;
    };

    461913-20160806163200793-1806847633

    单继承(父类含虚函数)

    原则:

      1.子类和父类各自拥有一个虚函数表

      2.若子类并没有overwrite父类虚函数,那么子类就使用父类虚函数

      3.若子类overwrite了父类虚函数,则子类虚函数将覆盖虚子类虚函数表中对应的父类虚函数

      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();
    
        return 0;
    }
    /*
    程序运行结果:
    
    Base fun1
    Derive fun2
    
    */

    QQ图片20190917171937

     

    一般多继承(不考虑菱形继承的多继承,因为菱形继承需要用到虚继承,之后讨论)

    原则:

      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;   //虚表指针大小+sizeof(int)
        cout << "b2:" << sizeof(b2) << endl; //虚表指针大小+sizeof(int)
        cout <<"d:" << sizeof(d) << endl;    //两个虚表指针大小+3*sizeof(int)
    
        return 0;
    }
    /*
    程序结果:
    b1:8
    b2:8
    d:20
    */

    QQ图片20190917174548

     

    简单虚继承

    虚继承可以解决菱形继承的情况

    原则:

      1.虚继承的子类,如果本身定义了新的虚函数,则编译器为其生成一个新的虚函数指针(vptr)以及一张虚函数表,该vptr位于对象内存的前面(对比非虚继承:直接扩展父类虚函数表)

      2.虚继承的子类也单独保留了父类的vptr和虚函数表

      3.虚继承的子类有虚基类表指针(vbptr)

    ps:在c++对象模型中,虚继承而来的子类会生成一个隐藏的虚基类指针(vbptr),虚基类表指针总是在虚函数表指针之后,因而对于某个实例来说,如果它有虚基类指针,那么虚基类指针可能在0字节偏移处(该类没有vptr,vbptr就位于实例内存布局的最前面,否则vptr位于最前面),也可能存在类实例的4字节偏移处

    461913-20160806182912481-2027577739

    461913-20160806182928247-1108080872

    样例:

    #include <iostream>
    using namespace std;
    
    class Base
    {
    public:
        virtual void fun1() {}
        virtual void fun2() {}
    private:
        int m_base;
    };
    
    class Derive : virtual public Base
    {
    public:
        void fun1() {}
        virtual void fun3() {}
    private:
        int m_derive;
    };
    
    int main()
    {
        Base b;
        Derive d;
        return 0;
    }

    QQ图片20190917195236

    菱形虚继承

    菱形虚继承是多继承和虚继承的复合

    内存模型如下:

    QQ图片20190917200639

     

  • 相关阅读:
    关于putty使用详解
    ORA12505: TNS: 监听程序当前无法识别连接描述符中所给出的SID等错误解决方法
    VMware的“Bridge”、“NAT”、“Hostonly”上网方式的区别
    转贴一个很全的sql用法
    网站
    sql exsits问题!!
    petshop4.0 profile
    一些应该知道的
    继承System.Web.UI.Page的页面基类
    应用程序域(Application Domain)
  • 原文地址:https://www.cnblogs.com/yinbiao/p/11536673.html
Copyright © 2011-2022 走看看