一,继承
1,继承定义
继承是C++三大特性之一。C++有类型的复用:类型模板,函数的复用:函数重载。而继承其本质是一种类的复用,使得程序员可以在原有类特性之上进行扩展来产生新的类,原有的类称为父类或基类,扩展出现的类称为子类或派生类。
继承后子类所有成员与成员函数都会成为子类的一部分。
定义父类,然后选择继承方式定义子类进行继承。
所谓继承方式就是程序员想要子类以何种权限看待父类中的成员,共存在三种继承方式:public,private,protected。类中成员也有相同的三种权限,我们可以将其一 一对应组成继承九宫格,每格对应每种继承情况。
基于这张表可以总结出:
(1)对于父类中的private成员,不管子类以何种方式继承,该成员在子类中都无法访问。
(2)对于其除private外的成员,他们在子类中的权限为:其自身在父类内的权限与子类的继承方式取两者最小的权限。
2,父,子类赋值转换
我们来看子类和父类对象之间的赋值操作:
我们分别定义父类对象p,子类对象s;如果我们将s赋值给p,其在编译器内部操作和其他并不一致。通常情况,不同类型变量间的赋值操作是:编译器内部操作是创建一个临时变量,然后将要赋的值拷贝在这个临时变量内,再将临时变量内的值拷贝到要赋值的变量中去。
当赋值操作左右两边是父类与子类对象时,编译器运用一种“切片”操作,编译器不创建临时变量,而是将子类中继承的成员的值直接拷贝到父类内,形象地如同将它切出来一样。
当我们用子类对象赋值父类对象的引用或指针时,例如person& ps = s, person* pp = s;此时的操作是编译器将直接让ps变成s继承p的那一片成员的别名,而pp将会指向s继承p的那一片成员的地址。
2,继承中的作用域
继承对作用域做出了如下规定
(1)子类和父类的作用域互相独立。
(2)若子类和父类存在同名成员,此时子类将会屏蔽对该父类成员的直接访问,称为隐藏。如果用户需要访问的是父类的同名成员,可以使用作用域符 父类::父类成员 进行访问。
3,继承体系中的四种默认成员函数
默认构造函数:
(1)对于非继承的对象,默认构造函数对内置类型不处理,对自定义类型调用它的默认构造函数。
(2)子类中的默认构造函数会调用父类的默认构造函数来初始化自己从父类继承下来的成员。
我在父类和子类中显示地写了默认构造函数,如果某个类调用了它自己的默认构造函数,它将输出“我是xx类的默认构造函数”。运行如下:
析构函数:
(1)编译器会对子类和父类的析构函数进行特殊处理:将这两个类的析构函数函数名都转换成:~destructor(),因此子类析构函数与父类析构函数构成隐藏。
(2)子类的析构函数也会调用父类的析构函数,子类的析构函数会优先进行调用,然后自动调用父类的析构函数。
拷贝构造:
(1)子类的拷贝构造会调用父类的拷贝构造对继承下来的成员进行初始化。
赋值重载:
(1)子类的赋值操作会调用父类的赋值重载函数对继承下来的成员进行初始化。
4,多继承:
C++中支持多继承,一个子类可以继承多个父类,如果这多个父类又是由同一个子类继承而来,那么这种继承就是菱形继承。当多个父类从同一个子类继承相同成员时,这多个父类又被一个子类继承,那么对于该子类继承下来的数据,不知道该访问哪个父类的成员这就是数据的二义性。
二,多态
1,多态定义
多态指不同对象完成某个动作时会有不同的行为,多态是类的一种行为的接口。
多态需要满足两个条件:
(1)父类中需要有虚函数,并在子类进行重写。
(2)在(1)的情况下必须通过父类的指针或者引用对虚函数进行调用。
该函数在不同的类内被分别重写了。如果使用类对象调用虚函数只会发生普通调用,调用的是父类未被重写的函数。
满足多态的情况下有两种调用:
使用普通调用:使用类对象调用函数,调用的函数只与类型有关。
使用多态调用:使用对象指针或引用调用和指向的类型有关。
例如:
定义了一个cat对象c,并将animal*指向c。pa是animal*类型,指向的是c的地址。此时我用pa调用animalbark,发生的是多态调用,其调用只取决于pa指向的地址,pa指向的c的地址,因此调用的是c内成员函数,尽管pa是animal*类型。
而c使用普通调用,调用只与类型有关,因此调用c内成员函数
抽象类
抽象类是一种特殊的类,该类不能实例化对象,它只是作为其他类的接口。抽象类中的虚函数称为纯虚函数,规定子类必须对纯虚函数进行重写,否则报错。