多态
- 1、虚析构
- 1.1、知识点的引入
- 1.2、解决上面的问题 虚析构 (虚函数)
- 2、纯虚函数 和抽象类
- 2.1、抽象类 派生出 子类,那么在子类必须实现所有的纯虚函数
- 案例:饮料制作
- 3、纯虚析构
- 纯虚函数:不需要实现函数体
- 纯虚析构:必须实现函数体
- 4、虚函数 纯虚函数 虚析构 纯虚析构(重要)
- 4.1、虚函数:只是virtual修饰有函数体 (作用于成员函数)
- 目的:通过基类指针或引用 操作 子类的方法
- 4.2、纯虚函数:virtual修饰 加=0 没有函数 所在的类为抽象类
- 目的:为子类提供固定的流程和接口
- 4.3、虚析构:virtual修饰 类中的析构函数
- 目的:为了解决基类的指针指向派生类对象,并用基类的指针删除派生类对 象
- 4.4、纯虚析构:virtual修饰 加=0 必须实现析构的函数体
- 目的:用基类的指针删除派生类对象、同时提供固定接口
- 5、重载 重定义 重写(了解)
- 5.1、重载
- 5.2、重定义(隐藏)
- 5.3、重写(覆盖)
1、虚析构
一份耕耘,一份收获,努力越大,收获越多。
C++的三大特性:
- 封装性:把客观的实物抽象成一个类(将数据和方法打包在一起,加以权限区分,达到保护并安全使用数据的目的)
- 继承: 继承所表达的是类之间相关的这种关系使得对象可以继承另一个类对象的特征和能力,目的:避免公用代码的重复开发相同的代码,减少代码和数据冗余。
- 多态:多态性可以简单地概括为“一个接口,多种方法”,字面意思为多种形态。程序在运行时才决定调用的函数,它是面向对象编程领域的核心概念。
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、重定义(隐藏)
重定义 (redefining)也叫做隐藏:
子类重新定义父类中有相同名称的非虚函数 ( 参数列表可以不同 ) 。
为什么要重载?由于函数重载可以在同一个作用域内,使用同一个函数名 命名一组功能相似的函数,这样做减少了函数名的数量,避免了程序员因给函数名命名所带来的烦恼,从而提高程序的开发的效率。
有继承
子类(派生类)重新定义父类(基类)的同名成员(非virtual函数)
class Base{
public:
void fun(int){}
void fun(int,int){}
}
class Son:public Base{
public:
void fun(参数可以不同){}//重定义
}
5.3、重写(覆盖)
函数特征相同。但是具体实现不同,主要是在类继承关系中出现的 。当我们对别人提供好的类的方法感觉不是太满意时,我们就可以通过继承这个类然后重写其方法改成我们需要的逻辑。
1、最重要的一点,重写是子类与父类之间的。
2、被重写的函数不能是 static 的。
3、函数三要素(函数名、函数参数、函数返回类型)完全一样。
4、如果父类中有virtual关键字,这种父子之间的关系叫做虚函数重写,这也就是C++中的多态机制。
有继承
子类(派生类)重写父类(基类)的virtual函数
函数返回值,函数名字,函数参数,必须和基类中的虚函数一致
class Base{
public:
virtual void fun(int){}
}
class Son:public Base{
public:
virtual void fun(int){}//重写
}