一、什么是继承
1、当遇到问题时,先查看现有的类能否解决一部分问题,如果有则继承该类,在此类的基础上进行扩展来解决问题,以此可以缩短解决问题
的时间
2、当遇到一个大而复杂的问题时,可以先把复杂的问题拆分成诺干个小问题,然后为每个小问题设计一个类进行解决,最后通过继承的方式
把这些类汇总到一个类中,从而解决最终的大问题的,以此降低问题的难度,也可以同时让多个程序员一起工作解决问题
子类继承父类 派生类继承基类
二、继承的语法
1、继承表
class Son : 继承表[继承方式 父类名1,继承方式 父类名2...]
{
成员变量;
public: //访问控制权限
成员函数;
};
2、继承方式
public
protected
private
三、C++继承的特点
1、C++中的继承可以有多个父类
2、子类会继承父类的所有内容
3、子类对象可以向它的父类类型转换(使用父类类型指针或引用指向子类对象)(可以缩小),但是父类对象不能向它的子类类型转换(不能使用子类类型
指针或引用指向父类对象)(不能扩大)
Base* b = new Son; true
Base& b = son; true
Son* son = new Base; false
Son& son = base; false
4、子类会隐藏父类的同名成员,在子类中直接使用同名成员时,默认使用的是自己的同名成员
但是可以通过 父类名::同名成员 的方式指定访问父类的同名成员
5、子类与父类的同名函数是无法构成重载的,因为不在同一个作用域下,并且子类会隐藏同名的父类成员函数,因此只能通过域限定符的方式
访问父类的同名成员函数
6、在执行子类的构造函数时,会按照继承表中的顺序执行父类的构造函数,默认执行的是父类的无参构造,可以在子类的构造函数的初始化
列表中显示地调用父类的有参构造
Son(int num):Base(参数){} // 调用Base的有参构造,最后才执行子类的构造函数
7、在子类的析构函数执行后,才会调用父类的析构函数,会按照继承表顺序,逆序执行父类的析构函数
8、当子类执行拷贝构造时,默认也会调用父类的无参构造,但这是有问题的,因此需要在子类的拷贝构造函数的初始化列表中显示地调用
父亲的拷贝构造函数
Son(Son& that):Base(that) {} //调用Base的拷贝构造
9、当子类执行赋值操作时,默认不会调用父类的赋值操作函数,如果需要调用父类的赋值操作函数时,可以通过域限定符和赋值函数名
的方式调用
void Son::operator(Son& that)
{
父类名::operator(that); //调用父类的赋值函数
}
四、继承方式和访问控制属性
访问控制属性成员的访问范围
public: 可以在任意位置访问
protected: 只能在类内和子类中访问
private: 只能在类内访问
继承方式的影响:
1、父类的成员是否能在子类中访问,是在设计父类时的访问控制属性决定的,不受继承方式的影响
2、但是继承方式能够决定父类成员被子类继承后,在子类中变成什么样的访问控制属性,具体情况见表
3、只有使用public继承父类,父类的指针或引用才能指向子类对象(多态的基础)
五、多重继承和钻石继承
1、多重继承
当一个类继承了多个父类时,称为多重继承,会按照继承表顺序在子类中排列父类的内容,当把子类指针对象转换为父类指针时,编译器
会自动计算出该父类内容所在的位置,并让指针偏移到该位置,因此可能会出现转换后的父类指针与转换前子类指针不相同的情况
2、钻石继承
假如有一个类A,类B和类C都继承了类A,类D又同时继承了类B和类C,当子类的父类中有共同的祖先时,这种称为钻石继承
1、类B和类C中都有类A的内容
2、类D会继承类B和类C中的所有内容,就会导致类D中有两份类A的内容
3、当类D对象去访问类A中的成员时,编译器会产生歧义,不确定要使用的是哪份类A中的成员,因此编译不通过
3、虚继承
当使用 virtual 关键字修饰继承表时,此时变成虚继承,此时子类中就会多一个虚指针用于指向父类的内容,当这个子类被继承时,
孙子类中也会继承该指针,并且通过虚指针比较是否含有多份相同的祖先类,如果有则只保留一份
因此:通过虚继承可以在钻石继承中解决子类有多份祖先类成员的问题