学习记录——day42 多态
静态多态:函数重载、运算符重载
学习记录——day41 运算符重载-CSDN博客
学习记录——day39 C++对函数的扩充 函数重载 函数的默认参数 哑元-CSDN博客
动态多态:函数重写
一、多态的实现
父类的指针或引用可以指向子类的对象,进而调用子类中重写的父类的虚函数
二、函数重写 override
1、函数重写是发生在父子类中
2、要求在子类中重写与父类函数中函数原型相同的函数
原型相同:返回值类型、函数名、参数列表都相同
三、虚函数
1)定义格式:在定义函数前加关键字 virtual,此时的函数就是虚函数
2)需要在父类中定义虚函数,子类中可以进行重写也可以不重写
3)当一个类中,如果定义了虚函数,那么该类会增加一个指针的大小,这个指针指向虚函数表
4)如果一个类中的某个函数定义成虚函数,那么该类的子子孙孙类中的该函数都是虚函数,即使没有加virtual
四、overload(函数重载)、override(函数重写)、hide(隐藏)的相关概念
1、函数重载:函数名相同,形参列表必须不同
1)作用域相同
2)函数名相同
3)参数列表必须不同(个数、类型)
4)有无 virtual 都无所谓
5)跟返回值没有关系
2、函数重写:子类中重写父类的虚函数
1)作用域发生在父子类中
2)函数原型相同(返回值、参数个数、参数类型、函数名)
3)父类中的函数必须要有 virtual 关键字
3、函数隐藏:子类中定义与父类同名的函数
1)作用域发生在父子俩中
2)函数名相同
3)返回值可以不同
4)参数相同
5)没有virtual修饰
五、纯虚函数
1、对于有些类而言,类中的相关成员函数没有实现的意义,主要是让子类来完成重写操作的
2、以便于使用父类的指针或引用指向子类对象,调用子类中重写的父类的虚函数
3、我们就可以将这样的函数设置成纯虚函数
4、定义格式: virtual 返回值类型 函数名(形参列表) = 0;
5、要求子类中必须对这些纯虚函数进行重写
6、抽象类:包含纯虚函数的类叫做抽象类,抽象类是不能实例化对象的
7、如果包含纯虚函数的子类中没有重写其虚函数,那么其子类也是抽象类,子类中的该函数也还是纯虚函数
六、虚析构函数
1、当使用父类指针指向子类对象时,构造时会正常先构造父类后构造子类,但是在使用delete释放内存空间时,由于父类指针的作用域,只作用在子类的父类空间内,所以,只会调用父类的析构函数,子类自己的空间就泄露了
2、此时可以使用虚析构函数来解决:定义析构函数时,在函数头前面加关键字virtual即可
3、虚析构函数能正确引导delete关键字,在释放父类空间时,把子类的空间一并释放
4、如果父类的析构函数为虚析构函数,那么该类的子子孙孙类中的析构函数都是虚析构函数
七、示例
#include <iostream>
using namespace std;
class Shape
{
public:
Shape(){}
virtual ~Shape(){}//虚析构函数
//定义虚函数
virtual void show()
{
cout<<"Shape virtual show"<<endl;
}
};
class Circle:public Shape
{
private:
int radius;
public:
Circle(){}
Circle(int r):radius(r){}
~Circle(){}
void show() override//函数重写
{
cout<<"面积为:"<<radius*radius*3.14<<endl;
cout<<"周长为"<<2*3.14*radius<<endl;
}
};
class Rectangle:public Shape
{
private:
int h;
int w;
public:
Rectangle(){}
Rectangle(int h,int w):h(h),w(w){}
~Rectangle(){}
void show() override
{
cout<<"面积为:"<<h*w<<endl;
cout<<"周长为"<<(h+w)*2<<endl;
}
};
//定义全局函数调用
void fun(Shape& s)
{
s.show();
}
int main()
{
Circle c(5);
fun(c);
Rectangle r(3,5);
fun(r);
cout<<"************"<<endl;
Shape *ptr = &c;
ptr->show();//重写的虚函数
ptr->Shape::show();//原虚函数
return 0;
}