多态是C++面向对象的三大特征之一
1.分类
静态多态:函数重载和运算符重载属于静态多态,服用函数名。
动态多态:派生类和虚函数实现运行时多态。
2.区别
静态多态的函数地址早绑定--编译阶段确定函数地址。
动态多态的函数地址晚绑定--运行阶段确定函数地址。
3.出现的问题
我们想要的效果是输出“猫说话”,实际输出“动物在说话”,因为C++中允许父子类之间的类型转换,而且地址早绑定,在编译阶段就已经确定。
#include<iostream>
using namespace std;
#include<string>
class animal
{
public:void speak(){cout << "动物在说话" << endl;}
};
class cat:public animal
{
public:void speak(){cout << "猫在说话" << endl;}
};
void fun(animal &a)
{a.speak();
}
int main()
{cat a;fun(a);return 0;
}
4.解决方法
使用动态多态
1.原因:因为动态多态是地址晚绑定,只有在运行阶段才会绑定。
2.方法:将animal中的speak函数变为虚函数
3.代码实现
#include<iostream>
using namespace std;
#include<string>
class animal
{
public:virtual void speak(){cout << "动物在说话" << endl;}
};
class cat:public animal
{
public:void speak(){cout << "猫在说话" << endl;}
};
void fun(animal &a)
{a.speak();
}
int main()
{cat a;fun(a);return 0;
}
这个是用引用实现的动态多态
5.动态多态满足条件
1.有继承关系
2.子类重写父类的虚函数(重写定义:函数返回值类型,函数名,参数列表要完全相同。)
6.动态多态使用场景
父类的指针或引用执行子类对象
7.底层原理
就是指针介入,使后续可以更改了
8.案例一
1.题目
利用多态实现计算器
2.多态优点
如果想扩展新的内容,不需要修改源码,只需要添加新的代码就行,符合开闭原则,利于维护。
(开闭原则:对扩展进行开放,对修改进行关闭)
3.实现步骤
利用指针方法实现多态,上面的方法是利用引用实现多态。
4.代码实现
#include<iostream>
using namespace std;
#include<string>
class calculator
{
public:virtual int fun(){return 0;}int num1 = 10;int num2 = 10;
};
class jia:public calculator
{
public:int fun(){return num1 + num2;}
};
class jian :public calculator
{
public:int fun(){return num1 - num2;}
};
class cheng :public calculator
{
public:int fun(){return num1 * num2;}
};
void fun2()
{calculator* abc = new jia;abc->num1 = 10;abc->num2 = 10;cout << abc->num1 << "+" << abc->num2 << "=" << abc->fun() << endl;abc = new jian;abc->num1 = 10;abc->num2 = 10;cout << abc->num1 << "-" << abc->num2 << "=" << abc->fun() << endl;abc = new cheng;abc->num1 = 10;abc->num2 = 10;cout << abc->num1 << "*" << abc->num2 << "=" << abc->fun() << endl;
}
int main()
{calculator a;jia b;jian c;cheng d;fun2();return 0;
}
9.纯虚函数和抽象类
1.定义
在多态中,通常父类中的虚函数是毫无意义的,主要都是来调用子类重写的内容,因此可以将虚函数改为纯虚函数。(当类中有了抽象函数,这个类也叫做抽象类)
2.纯虚函数语法
virtual 返回值类型 函数名(参数列表)=0;
3.抽象类特点
1.无法实例化对象
2.子类必须重写抽象类。中的纯虚函数,否则也属于抽象类。
4.代码演示
#include<iostream>
using namespace std;
#include<string>
class calculator
{
public:virtual void fun() = 0;
};
class jia:public calculator
{
public:void fun(){cout << "子类调用" << endl;}
};
int main()
{jia b;calculator* abc = new jia;abc->fun();return 0;
}
5.案例二
1.题目
使用纯虚函数构造饮品
2.代码实现
#include<iostream>
using namespace std;
#include<string>
class abstractdrinking
{
public://煮水virtual void boil() = 0;//冲泡virtual void brew() = 0;//倒入杯中virtual void pourincup() = 0;//加入辅料virtual void putsth() = 0;//整体实现void makedrink(){boil();brew();pourincup();putsth();}
};
class tea:public abstractdrinking
{
public://煮水virtual void boil(){cout << "煮农夫山泉" << endl;}//冲泡virtual void brew() {cout << "放入茶叶" << endl;}//倒入杯中virtual void pourincup(){ cout << "入杯中" << endl;}//加入辅料virtual void putsth(){cout << "加蜂蜜" << endl;}
};
void gowork(abstractdrinking *abc)
{abc->makedrink();delete abc;//释放
}
void maketea()
{gowork(new tea);
}
int main()
{maketea();return 0;
}
10.虚析构和纯虚析构
1.作用
多态使用时,如果有子类开辟到堆区,那么父类指针在释放时无法调用到子类的析构代码,将父类中的析构函数改为虚析构和纯虚析构。
2.无虚析构和纯析构版
#include<iostream>
using namespace std;
#include<string>
class animal
{
public:animal(){cout << "animal构造函数调用" << endl;}~animal(){cout << "animal析构函数调用" << endl;}virtual void speak() = 0;
};
class cat:public animal
{
public:cat(string name){cout << "cat构造函数调用" << endl;Name = new string(name);}~cat(){if (Name != NULL){cout << "cat析构函数调用" << endl;delete Name;Name = NULL;}}string* Name;void speak(){cout << *Name<<"cat在说话" << endl;}
};
void fun()
{animal* abc = new cat("tom");abc->speak();delete abc;
}
int main()
{fun();return 0;
}
问题:cat的析构函数未被调用,所以Name的堆区未被释放,导致内存泄漏。
3.虚析构版代码
#include<iostream>
using namespace std;
#include<string>
class animal
{
public:animal(){cout << "animal构造函数调用" << endl;}virtual ~animal(){cout << "animal析构函数调用" << endl;}virtual void speak() = 0;
};
class cat:public animal
{
public:cat(string name){cout << "cat构造函数调用" << endl;Name = new string(name);}~cat(){if (Name != NULL){cout << "cat析构函数调用" << endl;delete Name;Name = NULL;}}string* Name;void speak(){cout << *Name<<"cat在说话" << endl;}
};
void fun()
{animal* abc = new cat("tom");abc->speak();delete abc;
}
int main()
{fun();return 0;
}
4.纯虚析构版代码
#include<iostream>
using namespace std;
#include<string>
class animal
{
public:animal(){cout << "animal构造函数调用" << endl;}virtual ~animal() = 0;virtual void speak() = 0;
};
animal::~animal()
{cout << "animal析构函数调用" << endl;
}
class cat:public animal
{
public:cat(string name){cout << "cat构造函数调用" << endl;Name = new string(name);}~cat(){if (Name != NULL){cout << "cat析构函数调用" << endl;delete Name;Name = NULL;}}string* Name;void speak(){cout << *Name<<"cat在说话" << endl;}
};
void fun()
{animal* abc = new cat("tom");abc->speak();delete abc;
}
int main()
{fun();return 0;
}
注意:纯虚析构要有声明和实现两部分组成。
4.区别
虚析构和纯虚析构区别:如果是纯虚析构,该类属于抽象类,无法实例化对象。
5.注意
如果子类中没有堆区数据,可以不用写虚析构和纯虚析构