#include<iostream>
using namespace std;
//多态分为两类
//1、静态多态:函数重载和运算符重载属于静态多态,复用函数名
//2、动态多态:派生类和虚函数实现运行时多态 一般C++中的多态只动态多态
//区别
//静态多态的函数地址是早绑定的 - 编译阶段确定函数地址
//动态多态的函数地址是晚绑定的 - 运行阶段确定函数地址
//动物类
class Animal
{
public:
//虚函数
virtual void speak()
{
cout << "动物在叫" << endl;
}
};
//猫类
class Cat :public Animal
{
public:
virtual void speak()//此处的virtual可写可不写
{
cout << "小猫在叫" << endl;
}
};
//狗类
class Dog : public Animal
{
public:
void speak()
{
cout << "小狗在叫" << endl;
}
};
//地址早绑定,在编译阶段就确定了函数地址 已经确定是Animal中的speak函数的地址
//若想执行让猫叫,那么此函数的地址就不能早绑定,即晚绑定
//动态多态满足条件
//1、有继承关系
//2、子类中要重写父类中的虚函数
//动态多态的使用
//父类的指针或者引用 执行子类对象
void doSpeak(Animal& animal)
{
animal.speak();
}
void test01()
{
Cat cat;
doSpeak(cat);//用父类的引用接收子类的对象 Animal & animal = cat;
Dog dog;
doSpeak(dog);
}
int main() {
test01();
system("pause");
return 0;
}
多态底层的操作逻辑
如何通过父类的引用或指针,接收子类的对象
在子类中重写父类中的虚函数后,虚函数指针vfptr指向的虚函数表中的函数地址被子类中的函数地址覆盖。
其中vfptr:v: virtual ; f : function ; ptr: pointer
#include<iostream>
using namespace std;
#include<string>
//利用普通写法和多态技术实现计算器
class Calculator
{
public:
int Num1;
int Num2;
Calculator(int a,int b)
{
Num1 = a;
Num2 = b;
}
int operator()(string oper)
{
if (oper == "+") { return Num1 + Num2; }
else if (oper == "-") { return Num1 - Num2; }
else if (oper == "*") { return Num1 * Num2; }
else if (oper == "/") { return Num1 / Num2; }
}
int getResult(string oper)
{
if (oper == "+") { return Num1 + Num2; }
else if (oper == "-") { return Num1 - Num2; }
else if (oper == "*") { return Num1 * Num2; }
else if (oper == "/") { return Num1 / Num2; }
//如果要扩展新的功能,如平方和,最小公倍数等,需要扩展源码
//在真实的开发中 提倡 开闭原则 即 对扩展进行开放,对修改进行关闭。
//修改源码,不利于代码的维护和扩展。
}
};
void test01()
{
//Calculator cal(a,b);
//int c = cal.getResult("+");
//cout << "Result = " << c << endl;
Calculator cal(10, 20);
cout << "Result = " << cal("/") << endl;//利用重载()写仿函数
}
//利用多态实现计算器
//实现计算机的抽象类
//多态优点:
//1、组织结构清晰
//2、可读性强
//3、对于前期和后期的扩展以及维护性高
class AbstractCalculator
{
public:
virtual int getResult()
{
return 0;
}
int m_Num1;
int m_Num2;
};
//加法计算器类
class AddCalculator :public AbstractCalculator
{
public:
virtual int getResult()
{
return m_Num1 + m_Num2;
}
};
//减法计算器类
class SubCalculator :public AbstractCalculator
{
public:
virtual int getResult()
{
return m_Num1 - m_Num2;
}
};
//乘法
class MultiCalculator :public AbstractCalculator
{
public:
virtual int getResult()
{
return m_Num1 * m_Num2;
}
};
void test02()
{
//多态使用条件
//父类指针或者引用指向子类对象
//加法
AbstractCalculator* abc = new AddCalculator;
abc->m_Num1 = 10;
abc->m_Num2 = 10;
cout << abc->m_Num1 << " + " << abc->m_Num2 << " = " << abc->getResult() << endl;
delete abc;
//减法
abc = new SubCalculator;//通过改变指针的所属的类来进行不同的计算
abc->m_Num1 = 10;
abc->m_Num2 = 10;
cout << abc->m_Num1 << " - " << abc->m_Num2 << " = " << abc->getResult() << endl;
delete abc;
//乘法
abc = new MultiCalculator;//通过改变指针的所属的类来进行不同的计算
abc->m_Num1 = 10;
abc->m_Num2 = 10;
cout << abc->m_Num1 << " * " << abc->m_Num2 << " = " << abc->getResult() << endl;
delete abc;
}
int main() {
test02();
system("pause");
return 0;
}
#include<iostream>
using namespace std;
#include<string>
class AbstractBase
{
public:
//纯虚函数
//只要有一个纯虚函数,这个类称为抽象类
//抽象类特点:
//1、无法实例化对象
//2、抽象类的子类,必须重写父类中的纯虚函数,否则也属于抽象类。
virtual void func() = 0;
void operator()()
{
cout << "听!" << endl;
}
};
class Cat:public AbstractBase
{
public:
void func()
{
cout << "小猫在叫" << endl;
}
};
class Dog :public AbstractBase
{
public:
void func()
{
cout << "小狗在叫" << endl;
}
};
void test01()
{
/*Cat c;
c.func();
Dog d;
d.func();*/
Cat c;
c();//子类可以继承父类中的操作符重载
c.func();
Dog d;
d();
d.func();
}
int main() {
test01();
system("pause");
return 0;
}
#include<iostream>
using namespace std;
class AbstractDrink
{
public:
virtual void putIn() = 0;
virtual void boil() = 0;
virtual void brew() = 0;
virtual void pour() = 0;
void makeDrink()
{
boil();
brew();
pour();
putIn();
}
};
class tea :public AbstractDrink
{
public:
void boil()
{
cout << "煮水ing" << endl;
cout << "..." << endl;
cout << "叮!水煮好了!" << endl;
}
void brew()
{
cout << "------------" << endl;
cout << "冲泡中..." << endl;
cout << "..." << endl;
cout << "叮!泡好了!" << endl;
}
void pour()
{
cout << "------------" << endl;
cout << "倒入杯中..." << endl;
cout << "倒好了!" << endl;
}
void putIn()
{
cout << "加入柠檬" << endl;
}
};
class coffee :public AbstractDrink
{
public:
void boil()
{
cout << "煮水ing" << endl;
cout << "..." << endl;
cout << "叮!水煮好了!" << endl;
}
void brew()
{
cout << "------------" << endl;
cout << "冲泡中..." << endl;
cout << "..." << endl;
cout << "叮!泡好了!" << endl;
}
void pour()
{
cout << "------------" << endl;
cout << "倒入杯中..." << endl;
cout << "倒好了!" << endl;
}
void putIn()
{
cout << "加入糖和牛奶" << endl;
}
};
void doWork(AbstractDrink * abs)//这里时多态的使用,用父类的指针接收子类的对象
{
abs->makeDrink();//一个模板,一个接口,提供多个
delete abs;
/*tea t;
cout << "****先开始做茶****" << endl;
t.boil();
t.brew();
t.pour();
t.putIn();
cout<<endl;
cout << "****下面开始做咖啡****" << endl;
coffee c;
c.boil();
c.brew();
c.pour();
c.putIn();*/
}
void test01()
{
doWork(new tea);
cout << "------------" << endl;
doWork(new coffee);
}
int main() {
test01();
system("pause");
return 0;
}
在这个案例中,有需要注意的几点:
1、可以在父类中写好模板,并写一个汇总函数,调用每个过程,可以节省后期的执行代码量
2、在多态的使用过程中,总是忘记要用父类的指针或者引用来接收子类的对象。习惯性直接调用子类中的成员函数。
3、可以尝试在堆区中开辟数据,创建对象,同时记得手动释放内存。
4.7.5 虚析构和纯虚析构
多态使用时,如果子类中有属性开辟到堆区,那么父类指针在释放时无法调用到子类的析构代码。
解决方式:将父类中的析构函数改为虚析构或者纯虚析构
虚析构和纯虚析构共性:
-
可以解决父类指针释放子类对象
-
都需要有具体的函数实现
虚析构和纯虚析构区别: -
如果时纯虚析构,该类属于抽象类,无法实例化对象
虚析构语法:
virtual ~类名(){}
纯虚析构语法:
virtual ~类名() = 0;