多态成立的条件:
- 有继承
- 子类重写父类虚函数
- 函数返回值类型、函数名、参数列表 必须和父类完全一致(析构函数除外)
- 子类中重写的虚函数virtual关键字可加可不加(最好加上)
- 类型兼容:父类指针或引用指向之类对象
虚函数:
虚函数的定义:
在父类中定义,定义虚函数的一般形式:
virtual 函数返回值类型 虚函数名 (形参列表)
{
函数体
}
虚函数的作用:
- 虚函数的作用是实现动态联编(晚绑定:在程序运行时才确定函数的调用地址);在定义了虚函数后可以在基类(父类)的派生类(子类)中对虚函数进行重新定义;在派生类中重新定义的虚函数应与基类中的虚函数具有相同的形参个数和形参类型(覆盖),以实现接口的统一。如果在派生类中没有对虚函数重新定义,则它继续继承基类的虚函数。
- 虚函数可以让成员函数操作一般化,用基类的指针指向不同的派生类的对象时,基类虚成员函数调用基类指针,则会调用其真正指向的对象的成员函数,而不是基类中定义的成员函数(只要派生类重写了该成员函数)。若不是虚函数,则不管基类指针指向哪个派生类对象,调用时都会调用基类中定义的那个函数。
实现动态联编的三个条件:
- 必须把动态联编的方法定义为类中的public属性的虚函数
- 类之间存在子类型关系,一般表现为一个类从另一个类公有派生而来
- 必须先使用基类指针指向子类型对象,然后直接或间接使用基类指针调用虚函数
纯虚函数:
纯虚函数的定义:
在基类中不能对虚函数给出有意义的实现,则把它声明为纯虚函数,它的实现留给该基类的派生类去做,定义虚函数的一般形式:
virtual 函数返回值类型 虚函数名 (形参列表)=0;
纯虚函数的作用:
为派生类提供一个一致的接口。
抽象类:
抽象类是指含有纯虚函数的类(至少有一个纯虚函数),该类不能创建对象(抽象类不能实例化),但是可以声明指针和引用,用于基础类的接口声明和运行时的多态。
抽象类中,既可以有抽象方法,也可以有具体方法或者叫非抽象方法。一个继承于抽象类的子类,只有实现了父类所有的抽象方法才能够是非抽象类。
接口:
接口是一个概念。它在C++中用抽象类来实现,在C#和Java中用interface来实现。
接口是专门被继承的。接口存在的意义也是被继承。和C++里的抽象类里的纯虚函数是相同的。不能被实例化。
定义接口的关键字是interface,例如:
public interface MyInterface{
public void add(int x,int y);
public void volume(int x,int y,int z);
}继承接口的关键字是implements,相当于继承类的extends。需要注意的是,当继承一个接口时,接口里的所有函数必须全部被覆盖。
当想继承多个类时,开发程序不允许,报错。这样就要用到接口。因为接口允许多重继承,而类不允许(C++中可以多重继承)。所以就要用到接口。
注意:接口类当中,不能有成员属性,只能有类常量和成员方法
虚函数和纯虚函数的区别:
使用虚函数和纯虚函数的原因:
虚函数:
为了方便使用多态特征
纯虚函数:
- 为了方便使用多态特征
- 在很多情况下,基类本身生成对象是不合情理的。例如,动物作为一个基类可以派生出老虎、孔雀等子类,但动物本身生成对象明显不合常理。
- 纯虚函数就是基类只定义了函数体,没有实现过程。
- 纯虚函数相当于接口,不能直接实例话,需要派生类来实现函数定义;
二者的区别:
- 类里声明为虚函数的话,这个函数是实现的,哪怕是空实现,它的作用就是为了能让这个函数在它的子类里面可以被重载,这样的话,编译器就可以使用后期绑定来达到多态了;
纯虚函数只是一个接口,是个函数的声明而已,它要留到子类里去实现。- 虚函数在子类里面也可以不重载的;但纯虚必须在子类去实现,这就像Java的接口一样。通常我们把很多函数加上virtual,是一个好的习惯,虽然牺牲了一些性能,但是增加了面向对象的多态性,因为你很难预料到父类里面的这个函数不在子类里面不去修改它的实现;
- 虚函数的类用于“实作继承”,继承接口的同时也继承了父类的实现。当然我们也可以完成自己的实现。纯虚函数的类用于“介面继承”,主要用于通信协议方面。关注的是接口的统一性,实现由子类完成。一般来说,介面类中只有纯虚函数的;
- 带纯虚函数的类叫抽象类,这种基类不能直接生成对象,而只有被继承,并重写其虚函数后,才能使用
抽象类和接口的区别:
- 抽象类可以有构造方法,接口中不能有构造方法;
- 抽象类中可以有普通成员变量,接口中没有普通成员变量;
- 接口里边全部方法都必须是abstract的,抽象类的可以有实现了的方法;
- 抽象类中的抽象方法的访问类型可以是public,protected,但接口中的抽象方法只能是public类型的,并且默认即为public abstract类型;
- 抽象类中可以包含静态方法,接口中不能包含静态方法;
- 抽象类和接口中都可以包含静态成员变量,抽象类中的静态成员变量的访问类型可以任意,但接口中定义的变量只能是public static final类型,并且默认即为public static final类型。