zoukankan      html  css  js  c++  java
  • 虚继承

    多继承和多重继承导致的问题?

    多继承:就是某一个类继承了好几个基类
    多重继承:就是类被一层一层的继承
    

    这里写图片描述

    在上面的这幅图中,类B会拷贝一份A的数据,类C会拷贝一份A的数据,那么这时候类D再继承B和C的话,就会在类D中存在两份A的拷贝 。这是绝对不允许的 。一是浪费空间,二是存在二义性

    举例如下:

    这里写图片描述

    农民工中就会存在两份人的数据 。

    虚继承的出现和实现原理 ?

    正因为上面所说的问题的出现,为了解决这个问题,提出了虚继承的概念 。 那么它是如何实现的呐?

    1. 虚继承底层实现原理与编译器相关,一般通过虚基类指针和虚基类表实现,每个虚继承的子类都有一个虚基类指针(占用一个指针的存储空间,4字节)和虚基类表(不占用类对象的存储空间)(需要强调的是,虚基类依旧会在子类里面存在拷贝,只是仅仅最多存在一份而已,并不是不在子类里面了);当虚继承的子类被当做父类继承时,虚基类指针也会被继承。

    2. 实际上,vbptr指的是虚基类表指针(virtual base table pointer),该指针指向了一个虚基类表(virtual table),虚表中记录了虚基类与本类的偏移地址;通过偏移地址,这样就找到了虚基类成员,而虚继承也不用像普通多继承那样维持着公共基类(虚基类)的两份同样的拷贝,节省了存储空间。

    3. 在这里我们可以对比虚函数的实现原理:他们有相似之处,都利用了虚指针(均占用类的存储空间)和虚表(均不占用类的存储空间)。

      • 虚基类依旧存在继承类中,只占用存储空间;虚函数不占用存储空间。

      • 虚基类表存储的是虚基类相对直接继承类的偏移;而虚函数表存储的是虚函数地址。

    参考博文:

    此篇博客有关于虚继承详细的内存分布情况
    http://blog.csdn.net/xiejingfa/article/details/48028491

    实例讲解实现原理

    我以上面的例子来解释一下虚继承的实现原理,其中工人和农民会以虚继承的方式继承人 。

    (1)在工人和农民中各自都会有一个虚基类指针与虚基类表
    这里写图片描述
    (2)那么他们是如何找到基类中的成员的呐?就是下面这样子的
    这里写图片描述
    (3)再来就是农民工以 public 方式继承工人和农民
    这里写图片描述
    可见,在这次的继承中并没有将虚基类表继承下来!其实想一下也很简单了,只要有指针指向就行了呗!

    (4)补充说明的是:虚基类表存储的是,虚基类相对直接继承类的偏移(农民工并非是虚基类的直接继承类,工人和农民才是)

    代码举例:

    <1> 不用虚继承时:

    #include<iostream>
    #include<vector>
    #include<string>
    using namespace std;
    class Person{
        public:
        Person(string theColor){
            m_strColor = theColor ;
            cout << "Person()  " << endl ;
        }
        virtual ~Person(){
            cout << "~Person "<< endl ;
        }
        void printColor(){
            cout << m_strColor << endl ;
            cout <<"Person ------ printColor "<< endl ;
        }
        protected:
        string m_strColor ;
    };
    class Farmer :public   Person  //颜色 blue 
    {
        public:
        Farmer(string thename = "jack" ,string color = "blue"):Person("Farmer"+color) 
        {
            m_strName = thename ;
            cout << "Farmer()" << endl ;
        }
        virtual ~Farmer(){
            cout << "~Farmer() " << endl ;
        }
        protected:
        string m_strName ;
    };
    class Worker :  public  Person  // 颜色 red  
    {
        public:
        Worker(string thecode  = "001" ,string color = "red "):Person("Worker"+color )
        {
            m_strCode = thecode  ;
            cout << "Worker() " << endl ;
        }
        virtual ~Worker(){
            cout << "~Worker " << endl ;
        }
        protected:
        string m_strCode  ;
    };
    class FarmerWorker : public Farmer ,public Worker 
    {
        public:
        FarmerWorker(string thename,
                     string thecode ,
                     string color):Farmer(thename,color),Worker(thecode,color )
        {
            cout << "FarmerWorker()" << endl ;
        }
        ~FarmerWorker(){ 
            cout << "~FarmerWorker()" << endl ;
        }
    };
    int main(void){
        FarmerWorker  *p = new FarmerWorker("liushegxi","666","yellow ");
        p->Farmer::printColor();
        p->Worker::printColor();
        delete p ;
        p = nullptr ;
        return 0 ;
    }

    执行结果:
    这里写图片描述

    可见,它的的确确是有两份”yellow”的!(Person中的数据成员m_strColor 有两份 )

    <2> 使用虚继承时:

    #include<iostream>
    #include<vector>
    #include<string>
    using namespace std;
    class Person{
        public:
        Person(string theColor){
            m_strColor = theColor ;
            cout << "Person()  " << endl ;
        }
        virtual ~Person(){
            cout << "~Person "<< endl ;
        }
        void printColor(){
            cout << m_strColor << endl ;
            cout <<"Person ------ printColor "<< endl ;
        }
        protected:
        string m_strColor ;
    };
    class Farmer : virtual  public   Person  //颜色 blue 
    {
        public:
        Farmer(string thename = "jack ",string color = "blue " ):Person(color) 
        {
            m_strName = thename ;
            cout << "Farmer()" << endl ;
        }
        virtual ~Farmer(){
            cout << "~Farmer() " << endl ;
        }
        protected:
        string m_strName ;
    };
    class Worker :  virtual  public  Person  // 颜色 red  
    {
        public:
        Worker(string thecode ="001",string color = "red " ):Person(color )
        {
            m_strCode = thecode  ;
            cout << "Worker() " << endl ;
        }
        virtual ~Worker(){
            cout << "~Worker " << endl ;
        }
        protected:
        string m_strCode  ;
    };
    class FarmerWorker : public Farmer ,public Worker 
    {
        public:
        FarmerWorker(string thename,
                     string thecode ,
                     string the_color):Farmer(thename,the_color),Worker(thecode,the_color),Person(the_color) //注意Person 也得显式的写出来
        {
            cout << "FarmerWorker()" << endl ;
        }
        ~FarmerWorker(){ 
            cout << "~FarmerWorker()" << endl ;
        }
    };
    int main(void){
        FarmerWorker  *p = new FarmerWorker("liushegxi","666","yellow ");
        p->Farmer::printColor();
        p->Worker::printColor();
        delete p ;
        p = nullptr ;
        return 0 ;
    }

    执行结果:
    这里写图片描述

    观察可得,Person 只被实例化了一次,那么它也就只能产生一份数据了,其他类中只有虚基类指针指向它而已。

  • 相关阅读:
    css变量
    es6的this指向
    Java面试题(包装类)
    moment笔记
    Class
    CSS斜切角
    Element.getBoundingClientRect()
    Do not mutate vuex store state outside mutation handlers.
    antd不想写那么多option怎么办
    解析URL参数
  • 原文地址:https://www.cnblogs.com/Tattoo-Welkin/p/10335302.html
Copyright © 2011-2022 走看看