C++类的使用(五)—— 多态

时隔数月,我决定把这个系列继续写下去。

多态,顾名思义,就是多种形态,多种类型。
那就奇怪了,难不成可以把一个类直接转成另一个类?
错,其实多态是指从一个继承系里的类型互相转换。
先看代码事例

#include <iostream>

class Base
{
public:
	virtual void output()
	{std::cout << "This is Base" << std::endl;}
};
class B: public A
{
public:
	virtual void output()
	{std::cout << "This is Derived" << std::endl;}
	void newOutput()
	{std::cout << "This is Derived(new)" << std::endl;}
};

void foo1(Base);
void foo2(Base *);
void foo3(Base &);

int main()
{
	Derived d;
	foo1(d);
	foo2(&d);
	foo3(d);
}

void foo1(Base a)
{
	std::cout << "In function `foo1': ";
	a.output();
	//!a.newOutput();
}
void foo2(Base *a)
{
	std::cout << "In function `foo2': ";
	a->output();
	//!a->newOutput();
}
void foo3(Base &a)
{
	std::cout << "In function `foo3': ";
	a.output();
	//!a.newOutput();
}

基于之前的学习,我认为对代码无需任何解释,直接看输出

In function `foo1': This is Base
In function `foo2': This is Derived
In function `foo3': This is Derived

事实上,在调用函数时,*C++*对d作了一个类型转换(cast)
Derived转为BaseDerived *转为Base *,将Derived转为Derived &转为Base &
那转换之后到底发生了什么?答案看输出就知道了
对于类型直接转换(foo1),成员函数output被转成了父类Baseoutput
对于类型指针的转换(foo2)以及类型引用的转换(foo3),成员函数output不变
但细心的读着会发现,我在每个函数都注释了一行//!...
如果把//!去掉,是会报错的,也就是说,无论哪种类型的转换,都不能保留不是父类的成员函数

同时,你还会发现,output函数的定义多了个virtual,这种函数叫虚函数,是父类鼓励子类重写(override)的函数,事实上还有一个函数叫纯虚函数,它的声明如virtual void foo() = 0;,也是成员函数,但不同的是它没有定义,所以含有纯虚函数的类叫抽象类(abstract class),是不能实例化(instantiate)的,但是如果它的子类实现了纯虚函数,那么这个子类是可以被使用的。
举个例子

class A
{
public:
	virtual void output() = 0; // 纯虚函数
};
class B
{
public:
	virtual void output() // 实现纯虚函数
	{std::cout << "This is B" << std::endl;}
};

int main()
{
	A a; // 报错,类A是抽象类,不能实例化
	A *pa; // 不报错
	A &ra; // 不报错
	pa = new A; // 报错,类A时抽象类
	pa = new B; // 不报错,类B实现了纯虚函数
	delete pa;
}

那么多态到底有什么用?
其实我已开始对多态也不是很了解
但自从我学习了C++的一个扩展语言Qt后,我一下子明白了多态的重要性
举个例子

#include <string>
class Student
{
public:
	std::string name();
	void setName(const std::string &name);	// 使用常引用传输比值传输快
	int id();
	void setId(int id); // 对于基本类型,引用分左值引用和右值引用,较为麻烦,还是使用值传输
	int age();
	void setAge(int age);
protected:
	std::string _name;
	int _id;
	int _age;
};
class Teacher
{
public:
	std::string name();
	void setName(const std::string &name);	// 使用常引用传输比值传输快
	int salary();
	void setSalary(int salary); // 对于基本类型,引用分左值引用和右值引用,较为麻烦,还是使用值传输
	int age();
	void setAge(int age);
protected:
	std::string _name;
	int _salary;
	int _age;
};

这里实现我就不写了,就是直接赋值,直接返回
现在看似没什么问题
但如果你想建立一个数据库,存了许许多多职业
或者你想给他们加一个walk函数,加一个sleep函数,加一个doJob函数,你要怎么管理?
事实上这是一个继承的问题,最好的方法就是写一个Person类,包含人的共性,然后将StudentTeacher函数继承Person,然后新增特异性的函数
但如果没有多态,怎么将一个Student转换为Person
有了多态,我们就可以这样写:void foo(Person *person);,而不用管这个person到底是Student还是Teacher,只管执行person->doJob();就行了
或许我这个例子不是很典型,但是如果你学过Qt,或者看过Qt的一些代码,你会发现,Qt的核心就是以多态为基础的
比如Qt中有一个类叫QObject,这个类是大部分Qt类的基类,那么它到底有什么用呢?
Qt中,有一个机制叫信号-槽机制,这个机制允许一个sender发射(emit)信号(signal),而一个receiver接受这个信号。但Qt不可能把senderreceiver的类型全部罗列一遍,那就以ZObject为基类,写了一个函数
int connect(QObject *sender, const char *signal, QObject *receiver, const char *slot);
这就是多态真正的用武之地

本序列的文章:
C++类的使用(一)
C++类的使用(二)—— explicit构造与const成员变量赋值
C++类的使用(三)—— 封装
C++类的使用(四)—— 继承
C++类的使用(五)—— 多态
C++类的使用(六)—— 判断继承

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值