19.C++之多态

学习目标:


学习内容:

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;
}

在这里插入图片描述
在这里插入图片描述


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值