zoukankan      html  css  js  c++  java
  • C++对象模型:单继承,多继承,虚继承

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

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

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

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

    一、类成员分类

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

    三、多继承

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

     

     (2)菱形继承

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

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

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

    调用看看。

      

    三、虚继承

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

    (1)简单虚继承

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

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

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

     

     

     

    int main()
    {
    B1 a;
        cout <<"B1对象内存大小为:"<< sizeof(a) << endl;
     
        //取得B1的虚函数表
        cout << "[0]B1::vptr";
        cout << "	地址:" << (int *)(&a)<< endl;
     
        //输出虚表B1::vptr中的函数
        for (int i = 0; i<2;++ i)
        {
            cout << "  [" << i << "]";
            Fun fun1 = (Fun)*((int *)*(int *)(&a) + i);
            fun1();
            cout << "	地址:	" << *((int *)*(int *)(&a) + i) << endl;
        }
     
        //[1]
        cout << "[1]vbptr "  ;
        cout<<"	地址:" << (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 << "	地址:" << (int *)(&a) + 2;
        cout << endl;
     
        //[3]
        cout << "[3]值=" << *(int*)((int *)(&a) + 3);
        cout << "		地址:" << (int *)(&a) + 3;
        cout << endl;
     
        //[4]
        cout << "[4]B::vptr";
        cout << "	地址:" << (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 << "	地址:	" << *((int *)*((int *)(&a) + 4) + i) << endl;
        }
     
        //[5]
        cout << "[5]B::ib=" << *(int*)((int *)(&a) + 5);
        cout << "	地址: " << (int *)(&a) + 5;
        cout << endl;

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

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

     

    int main()
    {
        D d;
        cout << "D对象内存大小为:" << sizeof(d) << endl;
     
        //取得B1的虚函数表
        cout << "[0]B1::vptr";
        cout << "	地址:" << (int *)(&d) << endl;
     
        //输出虚表B1::vptr中的函数
        for (int i = 0; i<3; ++i)
        {
            cout << "  [" << i << "]";
            Fun fun1 = (Fun)*((int *)*(int *)(&d) + i);
            fun1();
            cout << "	地址:	" << *((int *)*(int *)(&d) + i) << endl;
        }
     
        //[1]
        cout << "[1]B1::vbptr ";
        cout << "	地址:" << (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 << "	地址:" << (int *)(&d) + 2;
        cout << endl;
     
        //[3]
        cout << "[3]B2::vptr";
        cout << "	地址:" << (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 << "	地址:	" << *((int *)*((int *)(&d) + 3) + i) << endl;
        }
     
        //[4]
        cout << "[4]B2::vbptr ";
        cout << "	地址:" << (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 << "	地址: " << (int *)(&d) + 5;
        cout << endl;
     
        //[6]
        cout << "[6]D::id=" << *(int*)((int *)(&d) + 6);
        cout << "	地址: " << (int *)(&d) + 6;
        cout << endl;
     
        //[7]
        cout << "[7]值=" << *(int*)((int *)(&d) + 7);
        cout << "		地址:" << (int *)(&d) + 7;
        cout << endl;
     
        //间接父类
        //[8]
        cout << "[8]B::vptr";
        cout << "	地址:" << (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 << "	地址:	" << *((int *)*((int *)(&d) + 8) + i) << endl;
        }
     
        //[9]
        cout << "[9]B::id=" << *(int*)((int *)(&d) + 9);
        cout << "	地址: " << (int *)(&d) +9;
        cout << endl;
     
        getchar();
    }

     

  • 相关阅读:
    How To Use Google Logging Library (glog)
    段错误调试
    getline 使用
    remove_if筛选数组元素
    getline C++ Reference
    c++ Why remove_copy_if returns an empty vector? Stack Overflow
    About iClick
    哈工大社会计算与信息检索研究中心
    python的16进制和10进制间的转换
    毕业生 哈尔滨工业大学社会计算与信息检索研究中心 理解语言,认知社会
  • 原文地址:https://www.cnblogs.com/EvansPudding/p/12565699.html
Copyright © 2011-2022 走看看