C++类的使用(五)—— 多态
时隔数月,我决定把这个系列继续写下去。
多态,顾名思义,就是多种形态,多种类型。
那就奇怪了,难不成可以把一个类直接转成另一个类?
错,其实多态是指从一个继承系里的类型互相转换。
先看代码事例
#include 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
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++类的使用(六)—— 判断继承
本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!
