学习目标:
学习内容:
1.多态的种类
多态主要分为两类:
- 静态多态:函数重载、运算符重载属于静态重载,复用函数名;
- 动态多态:派生类和虚函数实现运行时多态。
静态多态和动态多态的区别:
- 静态多态的函数地址早绑定—编译阶段确定函数地址;
- 动态多态的函数地址晚绑定—运行阶段确定函数地址。
#include<iostream>
using namespace std;
class Animal
{
public:
void speak()
{
cout << "动物在唱歌!!!" << endl;
}
};
class Cat:public Animal
{
public:
void speak()
{
cout << "小猫在唱歌!!!" << endl;
}
};
void dowork(Animal& animal)
{
animal.speak();
}
void test()
{
Cat cat;
dowork(cat);
}
int main()
{
test();
system("pause");
return 0;
}
现在是地址早绑定,打印出“动物在唱歌”,属于静态多态
要想打印出“小猫在唱歌”,则必须在相关函数前加上关键字virtual,使其变成虚函数。
#include<iostream>
using namespace std;
class Animal
{
public:
virtual void speak()
{
cout << "动物在唱歌!!!" << endl;
}
};
class Cat:public Animal
{
public:
void speak()
{
cout << "小猫在唱歌!!!" << endl;
}
};
void dowork(Animal& animal)
{
animal.speak();
}
void test()
{
Cat cat;
dowork(cat);
}
int main()
{
test();
system("pause");
return 0;
}
动态多态满足的条件:
- 有继承关系;
- 子类要重写父类的虚函数。
重写:返回值类型,函数名,参数列表都要完全一致
动态多态的使用:父类的指针或者引用指向子类对象。
//使用父类的指针来指向子类的对象
#include<iostream>
using namespace std;
class Animal
{
public:
virtual void speak()
{
cout << "动物在唱歌!!!" << endl;
}
};
class Cat:public Animal
{
public:
void speak()
{
cout << "小猫在唱歌!!!" << endl;
}
};
void test()
{
Animal* abs = new Cat;
abs->speak();
}
int main()
{
test();
system("pause");
return 0;
}
2.多态案例之计算器类
案例描述:分别利用普通类和多态技术,实现两个操作数进行运算的计算器类。
2.1 普通类计算器
#include<iostream>
using namespace std;
#include<string>
class Calculator
{
public:
int m_num1;
int m_num2;
int GetResult(string oper)
{
if (oper == "+")
{
return m_num1 + m_num2;
}
else if (oper == "-")
{
return m_num1 - m_num2;
}
}
};
void test()
{
Calculator c;
c.m_num1 = 15;
c.m_num2 = 10;
cout << c.m_num1 << " + " << c.m_num2 << " = " << c.GetResult("+") << endl;
cout << c.m_num1 << " - " << c.m_num2 << " = " << c.GetResult("-") << endl;
}
int main()
{
test();
system("pause");
return 0;
}
在这种普通写法中,若想要扩充新的功能,需要修改源码。在实际开发中,提倡“开闭原则”:对扩展进行开发,对修改进行关闭。
2.2 多态类计算器
#include<iostream>
using namespace std;
#include<string>
class Calculator
{
public:
int m_num1;
int m_num2;
virtual int GetResult()
{
return 0;
}
};
class Add:public Calculator
{
public:
int GetResult()
{
return m_num1 + m_num2;
}
};
class Sub :public Calculator
{
public:
int GetResult()
{
return m_num1 - m_num2;
}
};
void test()
{
//加法
Calculator* abs = new Add;
abs->m_num1 = 15;
abs->m_num2 = 10;
cout << abs->m_num1 << " + " << abs->m_num2 << " = " << abs->GetResult() << endl;
//开辟的内存空间必须释放
delete abs;
//减法
Calculator* abs1 = new Sub;
abs1->m_num1 = 15;
abs1->m_num2 = 10;
cout << abs1->m_num1 << " - " << abs1->m_num2 << " = " << abs1->GetResult() << endl;
//开辟的内存空间必须释放
delete abs1;
}
int main()
{
test();
system("pause");
return 0;
}
通过本例,我们可以知道多态的优点:
- 组织结构清晰;
- 可读性强;
- 对于前期和后期的扩展以及维护性高。
3.纯虚函数和抽象类
在多态中,父类中虚函数的实现是没有意义的,主要是强调子类重写的内容。因此可以将虚函数改为纯虚函数。纯虚函数的语法:virtual 返回值类型 函数名 (参数列表)= 0
。在类中有了纯虚函数,该类也称为抽象类。抽象类的特点为:
- 无法实例化对象;
- 子类必须重写抽象类中的纯虚函数,否则也是抽象类。
#include<iostream>
using namespace std;
#include<string>
class Base
{
public:
virtual void func() = 0;
};
class Son:public Base
{
public:
void func()
{
cout << "Func()的调用!!!" << endl;
}
};
void test()
{
//Base b; //无法实例化对象--错误
//Son s; //子类必须重写父类中纯虚函数,否则无法实例化对象
Base* base = new Son;
base->func();
}
int main()
{
test();
system("pause");
return 0;
}
4.多态案例之制作饮品
制作饮品的大致流程:煮水–>冲泡–>倒入杯中–>加入辅料,利用多态技术实现本案例,提供抽象类制作饮品基类,提供子类制作咖啡。
#include<iostream>
using namespace std;
#include<string>
class AbstracDrinking
{
public:
virtual void Boid() = 0;
virtual void Brew() = 0;
virtual void PourInCup() = 0;
virtual void PutSomeThing() = 0;
void MakeDrink()
{
Boid();
Brew();
PourInCup();
PutSomeThing();
}
};
class Coffee:public AbstracDrinking
{
public:
void Boid()
{
cout << "\t煮矿泉水!" << endl;
}
void Brew()
{
cout << "\t冲泡咖啡!" << endl;
}
void PourInCup()
{
cout << "\t倒入保温杯!" << endl;
}
void PutSomeThing()
{
cout << "\t加入牛奶!" << endl;
}
};
void test()
{
AbstracDrinking* base = new Coffee;//
cout << "—————制作咖啡开始-————" << endl;
base->MakeDrink();
}
int main()
{
test();
system("pause");
return 0;
}
5.虚析构和纯虚析构
使用多态时,若子类中有属性开辟到堆区,则父类指针在释放时无法调用到子类的析构代码。解决方案为:将父类中的析构函数改成虚析构函数或者是纯虚析构函数。
虚析构函数与纯虚析构函数的共性为:
- 可以解决父类指针释放子类对象;
- 都需要有具体函数来实现;
虚析构函数与纯虚析构函数的共性为:
- 若为纯虚析构函数,则该类为抽象类,不能进行实例化对象;
虚析构函数的语法为:virtual ~类名(){};
纯虚析构函数的语法为: virtual ~类名()= 0;
#include<iostream>
using namespace std;
#include<string>
class Animal
{
public:
virtual void speak() = 0;
Animal()
{
cout << "Animal的构造函数的调用!!" << endl;
}
~Animal()
{
cout << "Animal的析构函数的调用!!" << endl;
}
};
class Cat:public Animal
{
public:
Cat(string name)
{
cout << "Cat的构造函数的调用!!" << endl;
m_Name = new string(name);
}
~Cat()
{
if (m_Name != NULL)
{
cout << "Cat的析构函数的调用!!" << endl;
delete m_Name;
m_Name = NULL;
}
}
void speak()
{
cout <<*m_Name << "小猫在唱歌!!" << endl;
}
string *m_Name;
};
void test()
{
Animal* animal = new Cat("Tom");
animal->speak();
delete animal;
}
int main()
{
test();
system("pause");
return 0;
}
为了解决上述问题,将父类中的析构函数改成虚析构函数或者纯虚析构函数
5.1父类中的析构函数改成虚析构函数
#include<iostream>
using namespace std;
#include<string>
class Animal
{
public:
virtual void speak() = 0;
Animal()
{
cout << "Animal的构造函数的调用!!" << endl;
}
virtual ~Animal()
{
cout << "Animal的析构函数的调用!!" << endl;
}
};
class Cat:public Animal
{
public:
Cat(string name)
{
cout << "Cat的构造函数的调用!!" << endl;
m_Name = new string(name);
}
~Cat()
{
if (m_Name != NULL)
{
cout << "Cat的析构函数的调用!!" << endl;
delete m_Name;
m_Name = NULL;
}
}
void speak()
{
cout <<*m_Name << "小猫在唱歌!!" << endl;
}
string *m_Name;
};
void test()
{
Animal* animal = new Cat("Tom");
animal->speak();
delete animal;
}
int main()
{
test();
system("pause");
return 0;
}
5.2父类中的析构函数改成纯虚析构函数
#include<iostream>
using namespace std;
#include<string>
class Animal
{
public:
virtual void speak() = 0;
Animal()
{
cout << "Animal的构造函数的调用!!" << endl;
}
virtual ~Animal() = 0;
};
Animal::~Animal()
{
cout << "Animal的析构函数调用!!"<<endl;
}
class Cat :public Animal
{
public:
Cat(string name)
{
cout << "Cat的构造函数的调用!!" << endl;
m_Name = new string(name);
}
~Cat()
{
if (m_Name != NULL)
{
cout << "Cat的析构函数的调用!!" << endl;
delete m_Name;
m_Name = NULL;
}
}
void speak()
{
cout << *m_Name << "小猫在唱歌!!" << endl;
}
string* m_Name;
};
void test()
{
Animal* animal = new Cat("Tom");
animal->speak();
delete animal;
}
int main()
{
test();
system("pause");
return 0;
}