一.继承是什么
继承实际上是一种关系的复用,比如在现实生活中,学生属于人类,学生就继承了人类的所有特性,大学生属于学生,那么大学生也就继承了学生的额所有特性
二.继承的表示
格式 :
class 类名 : 继承方式(public.private,protected) (被继承的)类名
被继承的类(Person)称为父类/基类,继承的类(Student)称为子类/派生类
class Person
{
protected:
string _name;
};
class Student : public Person
{
protected:
int _stunum;
};
int main()
{
Student s1;
return 0;
}
三.继承的意义
继承即复用,父类的所有成员都会变成子类的一部分
Person中的_name成员也成为了Student s1的一部分,值得说明的是,这里编译器为了使我们看的更清楚一些,在s1下面表示出Person下有一个_name,实际上,在子类中,父类的成员直接存储在子类成员的前面,并不会存储父类的类名
四 . 三种继承方式 public,private,protected
注意 : 当成员是私有的,在子类中都不可见,不可见是指称为子类的一部分,但是我们不可以访问
1)父类成员访问限定符对继承的影响
当父类的成员是共有的或者保护的,那么在子类中,这些继承来的成员是可见的(可访问的)
当父类的成员是私有的,那么在子类中,这些继承来的成员是不可见的(不可访问的)
如 : 当我们把Person类中的_name定义为私有成员时,编译时,编译器会告诉我们,无法访问私有成员
所以,当我们需要在子类中访问父类成员时,必须把父类的成员定义成共有或者保护的.
为了实现我们复用的目的,在继承的时候保护限定符用的多一些,很少用私有限定符
2)子类的继承方式对继承的影响
当我们使用共有继承时,子类继承来的成员的访问限定不变(和父类一样)
当我们使用私有继承(保护继承)时,子类继承来的成员的访问限定变为私有(保护)
如 : 当我们用保护的方式继承父类(Person)时,在main函数中,调用Student的公有成员,编译器也会报错
3)公有继承 , 私有继承和保护继承的区别:
如果Student是公有继承Person,那么在Student中,成员的访问限定不变
如果Student是保护继承Person,那么当学生类再次被大学生类所继承的时候,在大学生类里,学生类的成员是可见的
如果Student是私有继承Person,那么在下面的Student的子类中,Student类的成员是不可见的
4)默认继承方式
对于class,继承方式不写的话,默认为私有继承
对于struct,继承方式不写的话,默认为公有继承
五.复制兼容问题(切片)
1)了解 is-a 和 has-a
2)切片
注意:
a)切片的前提是 is-a 的关系,像如下的 has-a 的关系是不可以切片的
b) 在切片的过程中并没有类型的转换,是天然的切割,只是把父类需要的东西从子类中取出来;
所以Person& p = s; 也是可以发生的,因为他并不是隐式类型转换,没有生成具有常性的临时变量,所以p可以直接成为s中一部分的别名
切片总结:
a)子类对象可以赋值给父类的对象/父类的引用/父类指针 -> 建立在 is-a的基础上
b)父类对象不可以赋值给子类对象 -> 父类比子类少一部分,无法满足子类的需要
c)父类指针/引用不能复制给子类的指针/引用(可以强转) ->
Student* s = (Student*)&p
Student& s = (Student&)p;
虽然可以通过以上方式强转,但是会出现越界情况,在上面代码中,父类只有_name一个成员,在32为平台下只有4个字节,但是强转为Student类的时候,编译器就会向后看4个字节去找_stunum,但是实际上并没有后面4个字节的空间,就会产生越界