多态的概念: 当不同的对象调用同一个函数产生出不同的状态。
目录多态
协变析构函数 override & final重载、重写、重定义的区别抽象类接口继承和实现继承
构成多态的条件
多态是在不同继承关系的类对象,去调用同一函数,产生了不同的行为。比如B继承了A。A对象产生的行为和B对象不同。
要构成多态还有两个条件:
1.必须通过基类的指针或者引用调用虚函数2.被调用的函数必须是虚函数,且派生类必须对基类的虚函数进行重写
虚函数
被virtual 关键字修饰的成员函数。
class Person { public: virtual void show() { cout << "父类" << endl; } };
只有非静态成员函数才可以是虚函数,解决菱形继承使用到的虚继承和这里没有关系。
虚函数的重写
虚函数的重写(覆盖)指的是子类中有一个跟父类完全相同的虚函数(即子类虚函数与父类虚函数的返回值类型、函数名字、参数列表完全相同,称子类的虚函数重写了父类的虚函数。
class Person { public: virtual void show() { cout << "父类" << endl; } }; class Student : public Person { public: //子类的virtual 关键字可以不写,子类已经继承了父类的虚函数,默认已带有virtual属性 virtual void show() { cout << "子类" << endl; } };
重写中的特例
协变
子类重写父类虚函数时,与父类虚函数返回值类型不同。即父类虚函数返回父类对象的指针或者引用,子类虚函数返回子类对象的指针或者引用时,称为协变。
class A {}; class B :public A {}; class Person { public: virtual A* show() { cout << "父类" << endl; } }; class Student :virtual public Person { public: virtual B* show() { cout << "子类" << endl; } };
析构函数
子类和父类中的析构函数虽然名字不同,但是同样构成隐藏(重定义),编译器对类析构和子类析构作统一处理,都默认看为 destructor() ,为同一个函数。
class Person { public: ~Person(){} }; class Student :virtual public Person { public: ~Student(){} //构成隐藏 };
如果父类的析构函数为虚函数,此时子类析构函数只要定义,无论是否加virtual关键字,都与父类的析构函数构成重写,虽然父类与子类析构函数名字不同。虽然函数名不相同,看起来违背了重写的规则, 其实不然,这里可以理解为编译器对析构函数的名称做了特殊处理,编译后析构函数的名称统一处理成destructor。
override & final
final
final关键字修饰虚函数,禁止虚函数被重写,如果发生重写编译器会报错。
class Person { public: virtual A* show() final { cout << "父类" << endl; } }; class Student :virtual public Person { public: virtual B* show() { cout << "子类" << endl; } };
override
检查子类虚函数是否重写父类的虚函数,如果未重写则编译器报错。
class Person { public: virtual void show(int i) { cout << "父类" << endl; } }; class Student :virtual public Person { public: virtual void show(char c) override { cout << "子类" << endl; } };
此时编译器报错
重载、重写、重定义的区别
重载
两个函数在同一作用域函数名相同/参数列表不同
重写(覆盖)
两个函数分别在基类和派生类的作用域函数名/参数/返回值都必须相同(协变除外),两个函数必须是虚函数
重定义(隐藏)
两个函数分别在基类和派生类的作用域函数名相同两个基类和派生类的同名函数不构成重写就是重定义
抽象类
在虚函数的后写上=0,则这个函数为纯虚函数。包含纯虚函数的类叫做抽象类(也叫接口类),抽象类不能实例化出对象。子类继承后也不能实例化出对象,只有重写纯虚函数,子类才能实例化出对象。纯虚函数规范了子类必须重写,另外纯虚函数体现出了接口继承。
class Person { public: virtual void show() = 0; };
接口继承和实现继承
普通函数的继承是一种实现继承,子类继承了父类函数,可以使用函数,继承的是函数的实现。虚函数的继承是一种接口继承,子类继承的是父类虚函数的接口,目的是为了重写,达成多态,继承的是接口。所以如果不实现多态,不要把函数定义成虚函数。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)