继承

Posted cexo

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了继承相关的知识,希望对你有一定的参考价值。

  • 单重继承——一个派生类最多只能有一个基类。
  • 多重继承——一个派生类可以有多个基类
      ①、class 类名: 继承方式 基类1,继承方式 基类2,…. {….};
      ②、派生类同时继承多个基类的成员,更好的软件重用。
      ③、可能会有大量的二义性,多个基类中可能包含同名变量或函数。
  • 多重继承中解决访问歧义的方法
      基类名::数据成员名(或成员函数(参数表))
      明确指明要访问定义于哪个基类中的成员

下面用代码来说明下:

#include <iostream>
using namespace std;

class Bed {//
public:
    Bed(int weight) : weight_(weight) {

    }
    void sleep() {
        cout<<"sleep ..."<<endl;
    }
    int weight_;
};

class Sofa {//沙发
public:
    Sofa(int weight) : weight_(weight) {

    }
    void watchTV() {
        cout<<"watch TV ..."<<endl;
    }
    int weight_;
};

class SofaBed : public Bed, public Sofa {//沙发床,公有继承自床类与沙发类
public:
    void foldOut() {//可以折叠出来
        cout<<"foldOut ..."<<endl;
    }
    void foldIn() {//可以收缩起来
        cout<<"foldIn ..."<<endl;
    }
    int weight_;
};



int main(void) {
    SofaBed sofaBed;
    return 0;
}

编译:

因为需要初始化父类,所以需要在SofaBed中默认构造中去处理:

下面对其沙发床的weight_属性赋值,由于简单起见,它是public的,所以可以直接在类的外部去赋值:

可以用域运算符去访问:

但是,上面代码中有点不妥:

所以更改代码如下:

#include <iostream>
using namespace std;

class Furniture {//家具
public:
    Furniture(int weight) : weight_(weight) {

    }
    int weight_;
};

class Bed : public Furniture{//
public:
    Bed(int weight) : Furniture(weight) {

    }
    void sleep() {
        cout<<"sleep ..."<<endl;
    }
    
};

class Sofa : public Furniture{//沙发
public:
    Sofa(int weight) : Furniture(weight) {
        
    }
    void watchTV() {
        cout<<"watch TV ..."<<endl;
    }
};

class SofaBed : public Bed, public Sofa {//沙发床,公有继承自床类与沙发类
public:
    SofaBed(int weight) : Bed(weight), Sofa(weight) {

    }
    void foldOut() {//可以折叠出来
        cout<<"foldOut ..."<<endl;
    }
    void foldIn() {//可以收缩起来
        cout<<"foldIn ..."<<endl;
    }
    int weight_;
};



int main(void) {
    SofaBed sofaBed(10);
    /*sofaBed.weight_ = 10;
    sofaBed.weight_ = 20;*///ERROR,访问不明确

    //sofaBed.Sofa::weight_ = 10;
    //sofaBed.Bed::weight_ = 20;

    sofaBed.watchTV();
    sofaBed.foldOut();
    sofaBed.sleep();

    return 0;
}

编译运行正常,但是此时如果更改weight_属性呢?

仍然提示访问不明确,为什么呢?

所以解决方法还是用域访问去解决:

所以说多继承就会有二义性产生,那上面这个沙发类还是有问题呀,居然有两个weight_,那就没有办法解决了么?答案是否定的,解决办法请看下面

  • 当派生类从多个基类派生,而这些基类又从同一个基类派生,则在访问此共同基类中的成员时,将产生二义性——采用虚基类来解决。
  • 虚基类的引入
      用于有共同基类的场合
  • 声明
      以virtual修饰说明基类
      例:class B1:virtual public BB
  • 作用
    ①、主要用来解决多继承时可能发生的对同一基类继承多次而产生的二义性问题。
    ②、为最远的派生类提供唯一的基类成员,而不重复产生多次拷贝。

具体代码如下:

#include <iostream>
using namespace std;

class Furniture {//家具
public:
    Furniture(int weight) : weight_(weight) {

    }
    int weight_;
};

class Bed : virtual public Furniture{//
public:
    Bed(int weight) : Furniture(weight) {

    }
    void sleep() {
        cout<<"sleep ..."<<endl;
    }
    
};

class Sofa : virtual public Furniture{//沙发
public:
    Sofa(int weight) : Furniture(weight) {
        
    }
    void watchTV() {
        cout<<"watch TV ..."<<endl;
    }
};

class SofaBed : public Bed, public Sofa {//沙发床,公有继承自床类与沙发类
public:
    SofaBed(int weight) : Bed(weight), Sofa(weight) {

    }
    void foldOut() {//可以折叠出来
        cout<<"foldOut ..."<<endl;
    }
    void foldIn() {//可以收缩起来
        cout<<"foldIn ..."<<endl;
    }
    int weight_;
};



int main(void) {
    SofaBed sofaBed(10);
    sofaBed.weight_ = 20;

    sofaBed.watchTV();
    sofaBed.foldOut();
    sofaBed.sleep();

    return 0;
}

编译:

报错了,因为虚基类的构造函数会涉及到一些问题,这个在下面会学习到,这里为了说明用虚基类能解决二义性,先暂且去掉带参数的构造函数,让其代码编译通过:

#include <iostream>
using namespace std;

class Furniture {//家具
public:
    //Furniture(int weight) : weight_(weight) {

    //}
    int weight_;
};

class Bed : virtual public Furniture{//
public:
    //Bed(int weight) : Furniture(weight) {

    //}
    void sleep() {
        cout<<"sleep ..."<<endl;
    }
    
};

class Sofa : virtual public Furniture{//沙发
public:
    //Sofa(int weight) : Furniture(weight) {
    //    
    //}
    void watchTV() {
        cout<<"watch TV ..."<<endl;
    }
};

class SofaBed : public Bed, public Sofa {//沙发床,公有继承自床类与沙发类
public:
    //SofaBed(int weight) : Bed(weight), Sofa(weight) {

    //}
    void foldOut() {//可以折叠出来
        cout<<"foldOut ..."<<endl;
    }
    void foldIn() {//可以收缩起来
        cout<<"foldIn ..."<<endl;
    }
    int weight_;
};



int main(void) {
    //SofaBed sofaBed(10);
    SofaBed sofaBed;
    sofaBed.weight_ = 20;

    sofaBed.watchTV();
    sofaBed.foldOut();
    sofaBed.sleep();

    return 0;
}

编译运行这时就正常了,也不存在二义性了,而此时的类结构就变为了:


说明这个问题之后,再来解释刚才有带参数的构造的问题,如下:

  •  虚基类的成员是由最远派生类【对于我们编写的测试代码则言,就是指的最底部的派生类SofaBed类】的构造函数通过调用虚基类的构造函数进行初始化的。 
  • 在整个继承结构中,直接或间接继承虚基类的所有派生类,都必须在构造函数的成员初始化表中给出对虚基类的构造函数的调用。如果未列出,则表示调用该虚基类的默认构造函数。
    这个规则就是造成上面带参数的构造编译通不过的原因,根据这个规划来修改代码如下:
    #include <iostream>
    using namespace std;
    
    class Furniture {//家具
    public:
        Furniture(int weight) : weight_(weight) {
    
        }
        int weight_;
    };
    
    class Bed : virtual public Furniture{//
    public:
        Bed(int weight) : Furniture(weight) {
    
        }
        void sleep() {
            cout<<"sleep ..."<<endl;
        }
    
    };
    
    class Sofa : virtual public Furniture{//沙发
    public:
        Sofa(int weight) : Furniture(weight) {
            
        }
        void watchTV() {
            cout<<"watch TV ..."<<endl;
        }
    };
    
    class SofaBed : public Bed, public Sofa {//沙发床,公有继承自床类与沙发类
    public:
        SofaBed(int weight) : Bed(weight), Sofa(weight), Furniture(weight) {
    
        }
        void foldOut() {//可以折叠出来
            cout<<"foldOut ..."<<endl;
        }
        void foldIn() {//可以收缩起来
            cout<<"foldIn ..."<<endl;
        }
        int weight_;
    };
    
    
    
    int main(void) {
        SofaBed sofaBed(10);
        sofaBed.weight_ = 20;
    
        sofaBed.watchTV();
        sofaBed.foldOut();
        sofaBed.sleep();
    
        return 0;
    }

    这时就可正常编译了,由于基类Furniture没有提供默认构造函数,所以在它的所有派生类中,不管是直接或间接的都需要去显示的构造Furniture才行,这也就是问题之所在。

  • 在建立对象时,只有最远派生类的构造函数调用虚基类的构造函数,该派生类的其他基类对虚基类构造函数的调用被忽略。
    这个怎么理解呢?我们将上面代码中的构造函数都打印出来就明白了:
    #include <iostream>
    using namespace std;
    
    class Furniture {//家具
    public:
        Furniture(int weight) : weight_(weight) {
            cout<<"Furniture ..."<<endl;
        }
        ~Furniture() {
            cout<<"~Furniture ..."<<endl;
        }
        int weight_;
    };
    
    class Bed : virtual public Furniture{//
    public:
        Bed(int weight) : Furniture(weight) {
            cout<<"Bed ..."<<endl;
        }
        ~Bed() {
            cout<<"~Bed ..."<<endl;
        }
        void sleep() {
            cout<<"sleep ..."<<endl;
        }
    
    };
    
    class Sofa : virtual public Furniture{//沙发
    public:
        Sofa(int weight) : Furniture(weight) {
            cout<<"Sofa ..."<<endl;
        }
        ~Sofa() {
            cout<<"~Sofa ..."<<endl;
        }
        void watchTV() {
            cout<<"watch TV ..."<<endl;
        }
    };
    
    class SofaBed : public Bed, public Sofa {//沙发床,公有继承自床类与沙发类
    public:
        SofaBed(int weight) : Bed(weight), Sofa(weight), Furniture(weight) {
            cout<<"SofaBed ..."<<endl;
        }
        ~SofaBed() {
            cout<<"~SofaBed ..."<<endl;
        }
        void foldOut() {//可以折叠出来
            cout<<"foldOut ..."<<endl;
        }
        void foldIn() {//可以收缩起来
            cout<<"foldIn ..."<<endl;
        }
        int weight_;
    };
    
    
    
    int main(void) {
        SofaBed sofaBed(10);
        sofaBed.weight_ = 20;
    
        sofaBed.watchTV();
        sofaBed.foldOut();
        sofaBed.sleep();
    
        return 0;
    }

    编译运行:


    也就说明了其它派生类调用基类的构造函数就被忽略了,也就是这个意思~

以上是关于继承的主要内容,如果未能解决你的问题,请参考以下文章

26.Qt Quick QML-RotationAnimationPathAnimationSmoothedAnimationBehaviorPauseAnimationSequential(代码片段

Flask之模板之宏继承包含

java中封装,继承,多态,接口学习总结

php如何实现多继承?

如何覆盖继承的嵌套类中存在的虚拟方法

多线程 Thread 线程同步 synchronized