多态
1、虚析构
一份耕耘,份收获,努力越大,收获越多。
1.1、知识点的引入
class Animal{
public:
//虚函数 本质函数指针 不涉及继承时 指向自身函数(sleep)
virtual void sleep(void)
{
cout<<"动物在睡觉"<<endl;
}
Animal()
{
cout<<"animal构造"<<endl;
}
~Animal()
{
cout<<"animal析构"<<endl;
}
};
class Cat:public Animal{
public:
//虚函数 设计到继承 指针子类的sleep
virtual void sleep(void)
{
cout<<"小猫在睡觉!!喵喵"<<endl;
}
Cat()
{
cout<<"Cat的构造"<<endl;
}
~Cat()
{
cout<<"Cat的析构"<<endl;
}
};
void test01()
{
//通过基类 指针、引用 访问子类的成员函数
Animal *p = new Cat;
p->sleep();//调用的子类的sleep
//出现的问题:只能释放 父类析构
delete p;
}
运行结果:
原因分析:
1.2、解决上面的问题 虚析构 (虚函数)
虚析构作用:通过基类指针、引用 释放 子类的所有空间。
虚析构:在虚析构函数前 加virtual修饰
class Animal{
public:
//虚函数 本质函数指针 不涉及继承时 指向自身函数(sleep)
virtual void sleep(void)
{
cout<<"动物在睡觉"<<endl;
}
Animal()
{
cout<<"animal构造"<<endl;
}
//虚析构
virtual ~Animal()
{
cout<<"animal析构"<<endl;
}
};
class Cat:public Animal{
public:
//虚函数 设计到继承 指针子类的sleep
virtual void sleep(void)
{
cout<<"小猫在睡觉!!喵喵"<<endl;
}
Cat()
{
cout<<"Cat的构造"<<endl;
}
virtual ~Cat()
{
cout<<"Cat的析构"<<endl;
}
};
void test01()
{
//通过基类 指针、引用 访问子类的成员函数
Animal *p = new Cat;
p->sleep();//调用的子类的sleep
//如果设置成了 虚析构 就可以释放 子类以及父类的构造函数
delete p;
}
运行结果:
原理分析:
2、纯虚函数 和抽象类
纯虚函数格式:virtual void sleep(void) = 0;
- 如果一个类中拥有 纯虚函数 那么这个类 就是抽象类
抽象类 不能实例化对象。
void test02()
{
//Animal 抽象类 不能实例化 一个对象
//Animal ob;//err
}
2.1、抽象类 派生出 子类,那么在子类必须实现所有的纯虚函数
如果 漏掉一个 那个子类也是抽象
class Animal{
public:
//纯虚函数
//如果一个类中拥有 纯虚函数 那么这个类 就是抽象类
//抽象类 不能实例化对象
virtual void sleep(void) = 0;
Animal()
{
cout<<"animal构造"<<endl;
}
//虚析构
virtual ~Animal()
{
cout<<"animal析构"<<endl;
}
};
class Cat:public Animal{
public:
#if 1
//在子类中 必须实现 基类的纯虚函数
virtual void sleep(void)
{
cout<<"小猫在睡觉!!喵喵"<<endl;
}
#endif
Cat()
{
cout<<"Cat的构造"<<endl;
}
virtual~Cat()
{
cout<<"Cat的析构"<<endl;
}
};
void test01()
{
Animal *p = new Cat;
p->sleep();
delete p;
}
案例:饮料制作
//抽象类 提供一个固定的流程 接口
class AbstractDrinking{
public:
//烧水
virtual void Boil() = 0;
//冲泡
virtual void Brew() = 0;
//倒入杯中
virtual void PourInCup() = 0;
//加入辅料
virtual void PutSomething() = 0;
//规定流程
void MakeDrink(){
Boil();
Brew();
PourInCup();
PutSomething();
}
};
//制作咖啡
class Coffee : public AbstractDrinking{
public:
//烧水
virtual void Boil(){
cout << "煮农夫山泉!" << endl;
}
//冲泡
virtual void Brew(){
cout << "冲泡咖啡!" << endl;
}
//倒入杯中
virtual void PourInCup(){
cout << "将咖啡倒入杯中!" << endl;
}
//加入辅料
virtual void PutSomething(){
cout << "加入牛奶!" << endl;
}
};
//制作茶水
class Tea : public AbstractDrinking{
public:
//烧水
virtual void Boil(){
cout << "煮自来水!" << endl;
}
//冲泡
virtual void Brew(){
cout << "冲泡茶叶!" << endl;
}
//倒入杯中
virtual void PourInCup(){
cout << "将茶水倒入杯中!" << endl;
}
//加入辅料
virtual void PutSomething(){
cout << "加入食盐!" << endl;
}
};
//业务函数
void DoBussiness(AbstractDrinking* drink){
drink->MakeDrink();
delete drink;
}
void test01()
{
//制作 咖啡
DoBussiness(new Coffee);
//制作 茶水
DoBussiness(new Tea);
}
运行结果:
3、纯虚析构
纯虚函数:不需要实现函数体
//纯虚析构函数
class B{
public:
1、virtual修饰 加上=0
virtual ~B() = 0;
};
//2、必须实现 析构函数的函数体
B::~B(){}
原因:通过基类指针 释放子类对象时 先调用子类析构 再父类析构
(如果父类的析构不实现,无法实现调用)
class Base
{
public:
//纯虚析构函数
virtual ~Base()=0;
};
Base::~Base()
{
}
int main(int argc, char *argv[])
{
//Base ob;//不能实例化对象
return 0;
}
4、虚函数 纯虚函数 虚析构 纯虚析构(重要)
4.1、虚函数:只是virtual修饰有函数体 (作用于成员函数)
目的:通过基类指针或引用 操作 子类的方法
class Base
{
public:
virtual my_fun(void)
{
//有函数体;
}
}
4.2、纯虚函数:virtual修饰 加=0 没有函数 所在的类为抽象类
目的:为子类提供固定的流程和接口
class Base
{
public:
virtual my_fun(void)=0;
}
4.3、虚析构:virtual修饰 类中的析构函数
目的:为了解决基类的指针指向派生类对象,并用基类的指针删除派生类对 象
class Base
{
public:
virtual ~Base()
{}
}
4.4、纯虚析构:virtual修饰 加=0 必须实现析构的函数体
目的:用基类的指针删除派生类对象、同时提供固定接口
class Base
{
public:
virtual ~Base()=0;
}
Base::~Base()
{函数体}
5、重写 重载 重定义(了解)
5.1、重载
同一作用域的同名函数、参数个数,参数顺序,参数类型不同
和函数返回值,没有关系
const也可以作为重载条件 //do(const Teacher& t){} do(Teacher& t)
int fun(int a){}
int fun(int b,int c){}
int fun(char b,int c){}
5.2、重定义(隐藏)
有继承
子类(派生类)重新定义父类(基类)的同名成员(非virtual函数)
class Base{
public:
void fun(int){}
void fun(int,int){}
}
class Son:public Base{
public:
void fun(参数可以不同){}//重定义
}
5.3、重写(覆盖)
有继承
子类(派生类)重写父类(基类)的virtual函数
函数返回值,函数名字,函数参数,必须和基类中的虚函数一致
class Base{
public:
virtual void fun(int){}
}
class Son:public Base{
public:
virtual void fun(int){}//重写
}