天天看点

cpp继承中的那些事(上)

0x00 背景介绍

我们都知道(确性),面向对象的三大特性是封装、继承、多态,封装是基础,继承是关键,多态是补充,看吧,继承是关键,这么关键的东西我们当然有必要好好了解一下啦。

0x01 单继承和多继承

顾名思义,单继承就是派生类只有一个直接基类的继承方式,而多继承就是有多个直接基类的继承方式。而不管是哪种继承,派生类都拥有父类的所有成员(成员数据和成员函数)

#include <iostream>
class FATHER {
private:
    int fa = 10;
};
class MONTHER {
private:
  int mo = 20;
};
// 单继承
class SON:public FATHER {

};
// 多继承
class BABY:public FATHER,public MOTHET {
  
};

int main(){
  SON son;    // 拥有FATHER的所有成员
  BABY baby;  // 拥有FATHER和MOTHER的所有成员
  return 0;
}           

复制

0x02 继承方式和访问权限

和结构体相比,类就显得“自私”多了,人家结构体的默认访问权限和继承方式都是公开(public)的,这样它的数据在任一时刻任何域中都能被任意对象访问到,类则刚好相反,默认的访问权限和继承方式都是私有(private)的,也就是只给特定对象开放访问权限。

{% note warning modern %}这里的结构体是在c++的语法体系中,c语言的语法中并没有结构体继承这种说法。{% endnote %}

继承方式 基类成员特性 派生类成员特性 派生类对象访问情况
公有继承(public) publicprotectedprivate publicprotected不可直接访问 可直接访问不可直接访问不可直接访问
保护继承(protected) publicprotectedprivate protectedprotected不可直接访问 不可直接访问不可直接访问不可直接访问
私有继承(private) publicprotectedprivate privateprivate不可直接访问 不可直接访问不可直接访问不可直接访问

总结一下:

1.  公有继承并不会改变基类成员的访问权限,保护继承和私有继承会把公有和受保护的访问权限分别修改为受保护的和私有的
2.  只有共有访问权限下的成员能直接被访问到,其他都不可直接访问           

复制

#include <iostream>
// 基类
class FATHER {
public:
    void setC(int _c) {
        this->c = c;
    }
    int a;
protected:
    int b;
private:
    int c;
};
// 派生类A 公有继承
class SONA :public FATHER {
public:
    void FunA() {
        a = 200;
        b = 10;
        setC(5);
        //c = 5;基类中是私有的,无论怎么继承都不能访问
    }
};
// 派生类B 保护继承
class SONB :protected FATHER {
public:
    void FunA() {
        a = 200;
        b = 10;
        //c = 5;父类中是私有的,无论怎么继承都不能访问
    }
};
// 派生类C 私有继承
class SONC :private FATHER {
public:
    void FunA() {
        a = 200;
        b = 10;
        //c = 5;父类中是私有的,无论怎么继承都不能访问
    }
};
// 孙子类1 公有继承
class CTest1 :public SONB {
public:
    void FunTest() {
        a = 200;//CB是保护继承的,在这里能够继续访问
        b = 10;
        //c = 5;父类中是私有的,无论怎么继承都不能访问
    }
};
// 孙子类2 公有继承
class CTest2 :public SONC {
public:
    void FunTest() {
        //a = 200;CC是私有继承的,在这里就不能访问了
        //b = 10; CC是私有继承的,在这里就不能访问了
        //c = 5;父类中是私有的,无论怎么继承都不能访问
    }
};

int main() {
    SONA objA;  // 派生类A对象
    SONB objB;  // 派生类B对象
    SONC objC;  // 派生类C对象

    //公有继承也称之为接口继承,父类中是公有的,
    // 子类中还有公有的,接口依然是接口。

    objA.a = 0;

    //私有继承或者保护继承也称之为实现继承**
    //使得父类中的公有成员变成了私有或者保护 
    //子类就失去了父类的接口。变成了内部实现

    //objB.a = 0;
    //objC.a = 0;

    //私有继承和保护继承,都是实现继承
    //他们有什么区别呢??
    //私有继承下,原公有或者保护的成员,
    //都在子类中变为了私有成员,再往下继承,就会不可访问
    //保护继承下,原公有或者保护的成员,
    //在子类中都是保护数据,再往下继承,还能在类内继续访问

    return 0;
}           

复制

0x03 关于继承中的重定义问题

当两个类存在继承关系时:

  1. 基类和派生类有同名成员变量或者成员函数,在派生类实例化对象的时候,访问到的是派生类自己的成员。
  2. 如果存在成员变量和成员函数同名,那么只能访问派生类的成员变量。
  3. 如果基类和派生类中存在同名的成员函数,但是参数列表不同,那么只能访问子类的成员函数

{% note info modern %}无论是函数名同名,还是变量名同名,还是函数和变量名同名,或是函数参数不一样,都会发生重定义,基类中的标识符都会被隐藏,只能访问派生类自己的成员,如果想要使用基类中的同名成员,那么需要使用域作用符来指定作用域。{% endnote %}

0x04 关于构造和析构函数调用顺序问题

构造函数:

  • 只有基类和派生类的时候,先调用基类的构造,再调用自己的构造
  • 只有类成员的时候,先调用类成员的构造,再调用自己的构造
  • 既有继承关系,又有类成员的时候,先调用基类的构造,再调用成员变量,最后调用自己的构造函数

析构函数:

析构函数的调用顺序则刚好相反,如果父类或类成员只有有参构造,那么需要在子类的构造函数中给他们赋值,使用初始化参数列表即可。

这里是不是觉得类又很无私呢~最后构造自己,最先销毁自己(233)