多态–PolyMorphism
C++中的多态是指:由继承而产生的相关的不同的类,其对象对同一消息会作出不同的响应。
例子:
甲乙丙三个班都是高二年级,他们有基本相同的属性和行为,在同时听到上课铃声的时候,他们会分别走向3个不同的教室,而不会走向同一个教室。
多态实现的前提:复制兼容
赋值兼容
就是向上转型
派生类的对象引用指针可以赋值给基类,但是基类只能访问
派生类从基类赋值的成员
。
复制兼容的三种情况:
- 派生类的对象可以赋值给基类对象
- 派生类的引用可以初始化基类的引用
- 派生类的指针可以初始化基类的指针
由于赋值操作发生在主程序,所以要访问基类成员,派生类必须公有继承。
#include <bits/stdc++.h>
using namespace std;
class Base{
public:
Base(int x,int y):x(x),y(y){}
Base(const Base& v):x(v.x),y(v.y){}
Base& operator=(const Base& v){
if(this==&v){
return *this;
}
x = v.x;
y = v.y;
return *this;
}
void display() const{
cout << x << "," << y << endl;
}
protected:
int x, y;
};
class Derived:public Base{//公有继承才能赋值重载
public:
Derived(int x,int y,int z):Base(x,y),z(z){}
Derived(const Base& b,int z):Base(b),z(z){}
Derived(const Derived& m):Base(m),z(m.z){}
Derived& operator=(const Derived& m){
if(this==&m){
return *this;
}
//调用基类operator=
Base::operator=(m);
z = m.z;
}
void display() const{//隐藏基类同名函数,无视参数和返回值
Base::display();
cout << z << endl;
}
private:
int z;
};
int main(){
Base a(100, 200);
Base b(a);
Base c = a;
Derived da(11, 22, 33);
Derived db(a, 10);
Derived dc(da);
Derived dd = db;
//三种运算符赋值重载
a = dc;//派生类dc赋值基类a
Base &m = db;//派生类引用赋值基类引用m
Base *n = &da;//派生类指针赋值基类指针n
//三种拷贝赋值重载
Base x(dc);
Base &y(db);
Base *z(&dc);
x.display();
y.display();
z->display();
}
静态多态
函数重载就是一种静态多态,编译时期决定
void func(int a,double b){//编译:func_i_d cout << "a*b=" << a * b << endl; } void func(double a,double b){//编译:func_d_d cout << "a*b=" << a * b << endl; } int main(){ int a = 4; double b = 2.4; double c = 3.1; func(a, b); func(b, c); }
但是函数重载不能仅仅返回值不同,只能是参数个数或者参数类型的不同
动态多态
动态多态不发生在编译时期,发生在运行时期
💖动态多态能发生的三个必要条件:
- 基类中有虚函数
- 派生类覆写基类的虚函数
- 将派生类对象地址赋给基类的指针或引用,发生虚函数的调用
虚函数就是加上virtual声明,类内声明类外定义时候,类内声明加类外定义不加
覆写override,派生类实现虚函数就是覆写
类的定义
class shape{ public: shape(int x,int y):_x(x),_y(y){} //类内声明虚函数 virtual void display() const; protected: int _x, _y; }; //virtual不能在类外定义 void shape::display() const{ cout << _x << "," << _y << endl; } /*派生类1:圆类*/ class Circle:public shape{ public: Circle(int x,int y,int z):shape(x,y),radius(z){} virtual void display() const{//覆写 cout << _x << "," << _y << ":" << radius << endl; } private: int radius; }; /*派生类2:矩形类*/ class Rect:public shape{ public: Rect(int x,int y,int a,int b):shape(x,y),length(a),width(b){} void display() const{//virtual可以不加 cout << _x << "," << _y << ":" << length << "," << width << endl; } public: int length, width; };
主程序
用派生类赋值基类,基类实现相同的虚函数接口,但是实现的是派生类覆写的功能
int main(){ Circle aa(100, 200, 300); Rect bb(13, 34, 4, 56); shape *ps = &aa; ps->display();//输出圆aa的display() ps = new Rect(9, 10, 11, 12); ps->display();//输出矩阵的display() shape &rf = bb; rf.display();//输出矩阵bb的display() rf = aa; rf.display();//输出圆aa的display() return 0; }
小结:
-
虚函数声明需要加上virtual,但是类外定义不要写
-
派生类重新定义基类虚函数就叫覆写,
函数要求返回值相同,参数类型和参数个数都相同
也就是函数必须完全相同,同名同参同返回
-
派生类覆写基类的虚函数,加不加virtual都行
-
真正的多态只能指针才能实现(引用存在重新赋值的局限)
多个类覆写了虚函数,利用
派生类赋值的基类指针
调用函数依然指向派生类覆写的class A{ public: virtual void display() { cout << "AAAAAA" << endl; } }; class B:public A{ }; /*派生类2:矩形类*/ class C:public B{ public: void display() {//virtual可以不加 cout << "CCCCCC" << endl; } }; int main(){ C().display();//CCCCCC C c; B *pb = &c; pb->display();//CCCCCC A *pa = &c; pa->display();//CCCCCC return 0; }
区分:函数重载,隐藏和覆写
- 函数重载:同名不同参,返回值不区分
- 隐藏:父子类同名即覆盖,参数和返回值不区分
- 覆写:子类重新定义父类虚函数,要求同名同参同返回
纯虚函数
virtual 函数声明=0;
纯虚函数没有实现体
含有纯虚函数的类叫抽象基类,抽象基类不可以实例化
作用就是被继承,给派生类提供接口
继承纯虚函数的类没有实现纯虚函数,依然不可以被实例化
#include <bits/stdc++.h> using namespace std; class Base{ public: Base(int a,int b):x(a),y(b){} //纯虚函数 virtual void display() const = 0; protected: int x, y; }; class A:public Base{ public: A(int a,int b,int c):Base(a,b),val(c){} void display() const{ cout << x << "," << y << "," << val << endl; } private: int val; }; class B:public Base{ public: B(int a,int b,char c):Base(a,b),ch(c){} private: char ch; }; int main(){ A a(6, 7, 8); Base *ps = &a; ps->display(); //B b(1, 2, 'z');报错,B没有实现纯虚函数定义,所以B也是纯虚函数 }
析构函数
- 普通析构函数
class Animal{ public: Animal(); ~Animal(); virtual void voice() const = 0; }; Animal::Animal(){ cout << "call Animal()" << endl; } Animal::~Animal(){ cout << "call ~Animal()" << endl; } //cat class Cat:public Animal{ public: Cat() { cout << "call Cat()" << endl; } ~Cat() { cout << "call ~Cat()" << endl; } void voice() const{ cout << "miaomiaomiao" << endl; } }; //Dog class Dog:public Animal{ public: Dog() { cout << "call Dog()" << endl; } ~Dog() { cout << "call ~Dog()" << endl; } virtual void voice() const{ cout << "wangwangwang" << endl; } };
- 在栈上运行
int main(){ Dog dog; Cat cat; Animal *ani = &cat; ani->voice(); ani = &dog; ani->voice(); }
- 运行结果
和以前一样:构造时候调用自己构造函数前会调用基类构造函数,运行结束,程序会自动先析构派生类对象再析构基类对象
在堆上分配内存
int main(){ Animal *ani = new Dog; ani->voice(); delete ani; Animal *tmp = new Cat(); tmp->voice(); delete tmp; }
运行结果:
发现析构只能虚构基类的,不能虚构派生类指针内存
虚析构:就是为了析构父类指针时候,可以完全析构子类对象
含有虚函数的类,析构函数也应该定义虚析构函数
class Animal{ public: Animal(); virtual ~Animal();//虚析构 virtual void voice() const = 0; }; Animal::Animal(){ cout << "call Animal()" << endl; } Animal::~Animal(){ cout << "call ~Animal()" << endl; }
基类在析构函数前加上virtual
主程序
int main(){ Animal *ani = new Dog; ani->voice(); delete ani; Animal *tmp = new Cat(); tmp->voice(); delete tmp; }
运行结果
基类析构函数加上virtual后,delete基类指针就能析构派生类对象
虚函数的几个限制:
✅只有类的成员函数才能声明为虚函数
只有继承才涉及派生类覆写虚函数
❎静态成员函数不能是虚函数
因为静态成员函数是
类
所有的,而实现多态必须有对象
❎内联函数不能是虚函数
虚函数是多态在运行时链接,但是内联函数是编译时候链接的
❎构造函数不能是虚函数
因为多态依赖于对象,但是构造函数没有创建对象
加virtual编译器不通过
当然析构函数可以加virtual,而且含有虚函数的类还必须加