zoukankan      html  css  js  c++  java
  • c++继承汇总(单继承、多继承、虚继承、菱形继承)

     多重继承中,一个基类可以在派生层次中出现多次,如果一个派生类有多个直接基类,而这些直接基类又有一个共同的基类,则在最终的派生类中会保留该间接共同基类数据成员的多分同名成员。C++提供虚基类的方法使得在继承间接共同基类时只保留一份成员。

      1.通常,每个类至初始化自己的直接基类,在应用于虚基类的时候,这个初始化策略会失败。如果使用这个规则,则可能多次初始化虚基类。为了解决这个问题,从具有虚基类的类继承的类初始化进行特殊处理。在虚派生中,由最底层的派生类构造函数初始化虚基类。即使是最底层派生类的非直接虚基类,也在最底层的虚派生类中调用其构造函数。

      2.无论虚基类出现在继承层次中的任何地方,总是在构造非虚基类之前构造虚基类。代码如下:

    class A
    {
    public:
    A()
    {
    cout<<"A constructor!"<<endl;
    }
    };

    class B
    {
    public:
    B()
    {
    cout<<"B constructor!"<<endl;
    }
    };

    class C
    {
    public:
    C()
    {
    cout<<"C constructor!"<<endl;
    }
    };

    class D : public A, public B, virtual public C
    {
    public:
    D()
    {
    cout<<"D constructor!"<<endl;
    }
    };


    int _tmain(int argc, _TCHAR* argv[])
    {
    D d;
    return 0;
    }

    运行结果:

    可以看出,即使是C类在派生列表的最后,类C也是首先构造,因为C为虚基类。但是如果虚基类还有一个基类而且该基类不是虚基类,则必须先构造基类,代码如下:

    class A
    {
    public:
      A()
      {
        cout<<"A constructor!"<<endl;
      }
    };

    class B
    {
    public:
      B()
      {
        cout<<"B constructor!"<<endl;
      }
    };

    class C
    {
    public:
      C()
      {
        cout<<"C constructor!"<<endl;
      }
    };

    class E : public C
    {
    public:
      E()
      {
        cout<<"E constructor!"<<endl;
      }
    };

    class D : public A, public B, virtual public E
    {
    public:
      D()
      {
        cout<<"D constructor!"<<endl;
      }
    };


    int _tmain(int argc, _TCHAR* argv[])
    {
      D d;
      return 0;
    }

    运行结果:

    虽然E是虚基类而C不是,但是还是先构造了C,因为C是E的基类,在构造一个类之前,必须先构造基类,即使是虚继承也不例外。

     

     

     

     

     

     

     

     

     

    一、C++中的对象模型

    1、 概念

    语言中直接支持面向对象程序设计的部分;

    对于各种支持的底层实现机制。(没看懂……)

    2、 类中的成员分类

    a) 成员函数

      i. static function

      ii. non static function

      iii. virtual function

    b)  数据成员

      i. static member data

      ii. non static member data

    3、 C++对象模型

    a) 类对象内存布局中的包括

      i. non static member data

      ii. vptr(虚函数表指针)

      iii. vbptr(虚基类表指针)

    b) 不包括

      i. static member data(存储在静态存储区)

      ii. 成员函数(存储在代码区)

    c) virtual table

    简称vtbl。存放着指针,这些指针指向该类每一个虚函数。虚表中的函数地址将按声明时的顺序排列。vtbl在类声明后就形成了,vptr是编译器生成的。

    d) vptr的位置一般放在一个类对象的最前端。

    e) 虚基类表

    vbptr指向的表,用于存放虚继承中,虚基类存储相对于虚基类表指针的偏移量。

    二、继承类型

    1、普通继承(不包含虚函数)

    a、单继承

    复制代码
    class Base
    {
    public:
        Base (int a = 1):base(a){}
        void fun0(){cout << base << endl;}
        int base;
    };
    class Derive:public Base
    {
    public:
        Derive (int a = 2):derive(a){}
        void fun1(){cout << base1 << endl;}
        int derive;
    };
    复制代码

    b、多继承

    复制代码
    class Base1
    {
    public:
        Base1 (int a = 2):base1(a){}
        void fun1(){cout << base1 << endl;}
        int base1;
    };
    class Base2
    {
    public:
        Base2 (int a = 3):base2(a){}
        void fun2(){cout << base2 << endl;}
        int base2;
    };
    class Derive: public Base1, public Base2
    {
    public:
        Derive (int value = 4):derive (value){}
        void fun3(){cout << derive << endl;}
        int derive;
    };
    复制代码

    c、菱形继承

    按 Ctrl+C 复制代码
    按 Ctrl+C 复制代码

    注:菱形继承存在二义性问题,编译都不通过,只能通过指定特定基类的方式进行访问基类变量。

    Derive d;

           d.base =3; // 不正确

           d.Base1::base = 3; // 正确

    2、普通继承(包含虚函数)

      a、单继承(包含虚函数)

    复制代码
    class Base
    {
    public:
        Base (int a = 1):base(a){}
        virtual void fun0(){cout << base << endl;}
        int base;
    };
    class Derive:public Base
    {
    public:
        Derive (int a = 2):derive(a){}
        virtual void fun0(){};
        virtual void fun1(){cout << derive << endl;}
        int derive;
    };
    复制代码

    注:派生类中新增的虚函数追加到虚函数表后面。

    b、多继承(包含虚函数)

    复制代码
    class Base1
    {
    public:
        Base1 (int a = 2):base1(a){}
        virtual void fun1(){cout << base1 << endl;}
        int base1;
    };
    class Base2
    {
    public:
        Base2 (int a = 3):base2(a){}
        virtual void fun2(){cout << base2 << endl;}
        int base2;
    };
    class Derive: public Base1, public Base2
    {
    public:
        Derive (int value = 4):derive (value){}
        virtual void fun3(){cout << derive << endl;}
        int derive;
    };
    复制代码

    注:派生类中新增的虚函数,追加到第一个基类的虚函数表的后面。

           c、菱形继承(包含虚函数)

    复制代码
    class Base
    {
    public:
        Base (int a = 1):base(a){}
        virtual void fun0(){cout << base << endl;}
        int base;
    };
    class Base1:public Base
    {
    public:
        Base1 (int a = 2):base1(a){}
        virtual void fun1(){cout << base1 << endl;}
        int base1;
    };
    class Base2:public Base
    {
    public:
        Base2 (int a = 3):base2(a){}
        virtual void fun2(){cout << base2 << endl;}
        int base2;
    };
    class Derive: public Base1, public Base2
    {
    public:
        Derive (int value = 4):derive (value){}
        virtual void fun3(){cout << derive << endl;}
        int derive;
    };
    复制代码

    注:分析时,由上到下依次分析。存在二义性问题和内存冗余问题。

     3、虚继承(不包含虚函数)

    新增虚基类指针,指向虚基类表,虚基类表中首项存储虚基类指针的偏移量,接下来依次存储虚基类的偏移量(偏移量是相对于虚基类表指针的存储地址)。

       a、单虚继承(不包含虚函数)

    复制代码
    class Base
    {
    public:
        Base (int a = 1):base(a){}
        void fun0(){cout << base << endl;}
        int base;
    };
    class Base1:virtual public Base
    {
    public:
        Base1 (int a = 2):base1(a){}
        void fun1(){cout << base1 << endl;}
        int base1;
    };
    复制代码

       b、多虚继承(不包含虚函数)

    复制代码
    class Base1
    {
    public:
        Base1 (int a = 2):base1(a){}
        void fun1(){cout << base1 << endl;}
        int base1;
    };
    class Base2
    {
    public:
        Base2 (int a = 3):base2(a){}
        void fun2(){cout << base2 << endl;}
        int base2;
    };
    class Derive:virtual public Base1, virtual public Base2
    {
    public:
        Derive (int value = 4):derive (value){}
        void fun3(){cout << derive << endl;}
        int derive;
    };
    复制代码

      c、菱形虚继承(不包含虚函数)

       第一种形式:

    复制代码
    class Base
    {
    public:
        Base (int a = 1):base(a){}
        void fun0(){cout << base << endl;}
        int base;
    };
    class Base1:virtual Base
    {
    public:
        Base1 (int a = 2):base1(a){}
        void fun1(){cout << base1 << endl;}
        int base1;
    };
    class Base2:virtual Base
    {
    public:
        Base2 (int a = 3):base2(a){}
        void fun2(){cout << base2 << endl;}
        int base2;
    };
    class Derive:virtual public Base1, virtual public Base2
    {
    public:
        Derive (int value = 4):derive (value){}
        void fun3(){cout << derive << endl;}
        int derive;
    };
    复制代码

    注:分析派生类的内存分布时,也是由上到下分析。虚继承将基类置于内存末尾,但是置于末尾的顺序也有一定的次序。首先Base先放到末尾,然后Base1放到末尾,最后Base2放到末尾。

           第二种形式:

    复制代码
    class Base
    {
    public:
        Base (int a = 1):base(a){}
        void fun0(){cout << base << endl;}
        int base;
    };
    class Base1:virtual public Base
    {
    public:
        Base1 (int a = 2):base1(a){}
        void fun1(){cout << base1 << endl;}
        int base1;
    };
    
    class Base2:virtual public Base
    {
    public:
        Base2 (int a = 3):base2(a){}
        void fun2(){cout << base2 << endl;}
        int base2;
    };
    class Derive: public Base1, public Base2
    {
    public:
        Derive (int value = 4):derive (value){}
        void fun3(){cout << derive << endl;}
        int derive;
    };
    复制代码

    注:分析的原则,从上到下,依次分析。

    4、 虚继承(包含虚函数)

    a、单虚继承(包含虚函数)

    复制代码
    class Base
    {
    public:
        Base (int a = 1):base(a){}
        virtual void fun0(){cout << base << endl;}
        int base;
    };
    class Base1:virtual Base
    {
    public:
        Base1 (int a = 2):base1(a){}
        virtual void fun1(){cout << base1 << endl;}
        int base1;
    };
    复制代码

      与普通的包含虚函数的单继承相比,派生类拥有自己的虚函数表以及虚函数表指针,而不是与基类共用一个虚函数表。注意虚函数表指针和虚基类表指针的存储顺序。

      b、多虚继承(包含虚函数)

    复制代码
    class Base1
    {
    public:
        Base1 (int a = 2):base1(a){}
        virtual void fun1(){cout << base1 << endl;}
        int base1;
    };
    
    class Base2
    {
    public:
        Base2 (int a = 3):base2(a){}
        virtual void fun2(){cout << base2 << endl;}
        int base2;
    };
    class Derive:virtual public Base1, virtual public Base2
    {
    public:
        Derive (int value = 4):derive (value){}
        virtual void fun3(){cout << derive << endl;}
        int derive;
    };
    复制代码

    c、菱形虚继承(包含虚函数)

           第一种形式:

    复制代码
    class Base
    {
    public:
        Base (int a = 1):base(a){}
        virtual void fun0(){cout << base << endl;}
        int base;
    };
    class Base1:virtual public Base
    {
    public:
        Base1 (int a = 2):base1(a){}
        virtual void fun1(){cout << base1 << endl;}
        int base1;
    };
    
    class Base2:virtual public Base
    {
    public:
        Base2 (int a = 3):base2(a){}
        virtual void fun2(){cout << base2 << endl;}
        int base2;
    };
    class Derive:  public Base1, public Base2
    {
    public:
        Derive (int value = 4):derive (value){}
        virtual void fun3(){cout << derive << endl;}
        int derive;
    };
    复制代码

    第二种形式:

    复制代码
    class Base
    {
    public:
        Base (int a = 1):base(a){}
        virtual void fun0(){cout << base << endl;}
        int base;
    };
    class Base1:virtual public Base
    {
    public:
        Base1 (int a = 2):base1(a){}
        virtual void fun1(){cout << base1 << endl;}
        int base1;
    };
    
    class Base2:virtual public Base
    {
    public:
        Base2 (int a = 3):base2(a){}
        virtual void fun2(){cout << base2 << endl;}
        int base2;
    };
    class Derive: virtual public Base1,virtual public Base2
    {
    public:
        Derive (int value = 4):derive (value){}
        virtual void fun3(){cout << derive << endl;}
        int derive;
    };
    复制代码

    自行脑补C++类对象的内存结构……

    注:上述虚函数中,如果派生类重写了基类的虚函数,则对应虚函数表中的虚函数应该修改成重新后的虚函数,即Base::fun()->Derive::fun()。

  • 相关阅读:
    数组元素按指定的位置排序
    git修改历史提交的备注信息
    js常用遍历理解
    async await和promise的区别,和使用方法
    js检测邮箱格式,正则检测邮箱格式
    前端,es6中的promise异步方法,及用的场景
    JMter 压力测试工具简单使用及介绍
    Vue Config
    vue 文件上传
    Windows Redis集群搭建简单版
  • 原文地址:https://www.cnblogs.com/weekbo/p/8855475.html
Copyright © 2011-2022 走看看