时隔数月,我决定把这个系列继续写下去。
多态,顾名思义,就是多种形态,多种类型。
那就奇怪了,难不成可以把一个类直接转成另一个类?
错,其实多态是指从一个继承系里的类型互相转换。
先看代码事例
#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
转为Base
,Derived *
转为Base *
,将Derived
转为Derived &
转为Base &
那转换之后到底发生了什么?答案看输出就知道了
对于类型直接转换(foo1
),成员函数output
被转成了父类Base
的output
对于类型指针的转换(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
类,包含人的共性,然后将Student
和Teacher
函数继承Person
,然后新增特异性的函数
但如果没有多态,怎么将一个Student
转换为Person
?
有了多态,我们就可以这样写:void foo(Person *person);
,而不用管这个person
到底是Student
还是Teacher
,只管执行person->doJob();
就行了
或许我这个例子不是很典型,但是如果你学过Qt,或者看过Qt的一些代码,你会发现,Qt的核心就是以多态为基础的
比如Qt中有一个类叫QObject
,这个类是大部分Qt类的基类,那么它到底有什么用呢?
在Qt中,有一个机制叫信号-槽机制,这个机制允许一个sender
发射(emit
)信号(signal
),而一个receiver
接受这个信号。但Qt不可能把sender
和receiver
的类型全部罗列一遍,那就以ZObject
为基类,写了一个函数
int connect(QObject *sender, const char *signal, QObject *receiver, const char *slot);
这就是多态真正的用武之地
本序列的文章:
C++类的使用(一)
C++类的使用(二)—— explicit构造与const成员变量赋值
C++类的使用(三)—— 封装
C++类的使用(四)—— 继承
C++类的使用(五)—— 多态
C++类的使用(六)—— 判断继承