• [杂记]关于C++中类继承的一些理解


    这篇主要对继承类型和菱形继承问题做一个笔记。

    1. 继承类型

    继承类型有公有、保护和私有。

    1.1 公有继承

    公有继承可以继承父类中public和protected的成员与方法,是最简单的方式,如下表:

    父类中的成员:publicprotectedprivate能否隐式向上转换
    子类中:可以访问,成为子类的public可以访问,成为子类的protected不能访问,只能通过父类的函数接口访问可以

    其中隐式向上转换即能否用父类指针管理子类对象。

    1.2 私有继承

    如果我们想将另一个类(例如vector)作为这个类的一个成员, 最简单的方式是:

    class A{
    private:
    	std::vector<int> arr;
    	...
    public: 
    	int getValue(int pos) { return arr[pos]; }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    这种方式直观好用. 但如果设想这么一个情形: 假设我们作为成员的那个类有一个protected成员, 而且我们想访问它. 那么我们通过以上的方式是做不到的. 这时,我们可以通过private继承的方式:

    class A : private std::vector<int>{
    private:
    	
    	...
    public: 
    	A(std::vector<int> v) : std::vector<int>(v) {}
    	int getValue(int pos) { return std::vector<int>::at(pos); }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    来访问到protected的成员.

    当然at方法不是vector的protected成员, 只是举个例子

    private的性质如下表:

    父类中的成员:publicprotectedprivate能否隐式向上转换
    子类中:可以访问,成为子类的private可以访问,成为子类的private不能访问,只能通过父类的函数接口访问不可以

    1.3 保护继承

    保护继承的大部分行为应该与私有继承差不多, 但是当该类再派生类的时候, 就体现出区别了. 也就是说, 如果是私有继承, 则第一代类的成员到了第二代类都成了私有的, 因此第三代类无法访问. 而如果是保护继承, 则第一代类的成员到了第二代类仍然是保护的,所以到第三代类也能访问. 具体如下表:
    private的性质如下表:

    父类中的成员:publicprotectedprivate能否隐式向上转换
    子类中:可以访问,成为子类的protected可以访问,成为子类的protected不能访问,只能通过父类的函数接口访问可以

    2. 菱形继承问题

    菱形继承问题主要就是当一个类继承多个类, 而继承的多个类拥有共同的祖先, 则在该类中就会存在多个祖先的副本, 从而在访问祖先的成员时造成二义性.

    假设我们有一个Restaurant类, 有一个KFC类和McDonald类分别继承它. 然后再有一个Wallace类同时继承KFC和McDonald, 就会是这样的结构:

    在这里插入图片描述
    那么当我们实例化Wallace的时候, 就要分别调用KFC和McDonald的构造函数, 而KFC和McDonald又会分别构造一个Restaurant, 于是Wallace里面就成了这个样子:

    在这里插入图片描述
    所以它里面有两个Restaurant, 当然会产生二义性了.

    解决这个问题的方法是使用虚继承, 或者说把最远的基类变成虚基类. 当一个类被虚继承时, 它就成了虚基类. 为了避免有两个Restaurant, 一个直观的办法是, 我们在构造Wallace的时候, 在构造McDonald和KFC的时候不构造Restaurant, 而另外构造一个Restaurant, 这样每个类在Wallace中只有一份, 事实上如果使用虚继承, 则编译器就是这样做的.

    因此, 在构造Wallace类的时候, 不像普通继承那样只需要写父类的构造函数就行了, 这时必须要写父类的父类(Restaurant类)的构造函数, 这样才能达到每个祖宗都是一份的效果.

    例如下面的示例:

    class Restaurant{
    private: 
        std::string name;
        int price;
    public:
        Restaurant(std::string str, int p) : name(str), price(p) {};
        virtual ~Restaurant() {} ;
        virtual void getName() { std::cout << name << price << "\n"; };
        int returnPrice() { return this->price; };
    };
    
    class KFC : virtual public Restaurant{
    private: 
        bool crazyThursday;
    public:
        KFC(std::string str, int p, bool cT) : Restaurant(str, p), crazyThursday(cT) {};
        virtual ~KFC() {};
        virtual void getName() { this->Restaurant::getName(); std::cout << this->crazyThursday << "\n"; };
    
    };
    
    class McDanolds : virtual public Restaurant{
    private: 
        int OMCard;
    public: 
        McDanolds(std::string str, int p, int OMCard_) : Restaurant(str, p), OMCard(OMCard_) {};
        virtual ~McDanolds() {};
        virtual void getName() { this->Restaurant::getName(); std::cout << this->OMCard << "\n"; };
    };
    
    class Wallace : public KFC, public McDanolds{
    private:
        bool PensheSuperman;
    public: 
        Wallace(std::string str, int p, bool cT, int OMCard_, bool ps) :  Restaurant(str, p), KFC(str, p, cT), McDanolds(str, p, OMCard_) {};
        void getName() { this->Restaurant::getName(); std::cout << this->PensheSuperman << "\n"; };
        int getPrice() { return this->Restaurant::returnPrice();};
    };
    
    
    void func(){
        std::string str = "asdadasdw";
        Wallace w (str, 2, true, 2333, true);
        
        std::cout << w.getPrice();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46

    输出结果:

    2
    
    • 1
  • 相关阅读:
    SpringBoot中常用校验注解简介说明
    Java后端开发——实现登录验证程序
    redux太繁琐?一文入门学会使用mobx简化项目的状态管理
    网络安全(黑客)自学
    Codeforces Round #796 (Div. 2)(A~E题题解)
    DW学生美食网页设计作业——餐饮美食汉堡企业网站6页面带轮播(HTML+CSS+JavaScript)
    【安卓应用渗透】第一篇:安卓逆向回顾和梳理-2211
    Android 12 “Bug 连连”:除了一加、三星,谷歌自家手机都被“坑”了
    国产化正在成为超融合市场的重要发展方向之一
    基于混沌搜索策略的鲸鱼优化算法-附代码
  • 原文地址:https://blog.csdn.net/wjpwjpwjp0831/article/details/126866650