文档章节

多态

 野渡书生
发布于 2016/04/21 16:12
字数 1727
阅读 19
收藏 3

1、何为多态?

定义:

系统在运行时(而非编译时),能够根据其类型确定调用哪个重载的成员函数的能力,称为多态性。

特点:

(1)多态是面向对象的重要特性,简单点说:“一个接口,多种实现”,就是同一种事物表现出的多种形态。

(2)多态就是抽象化的一种体现,把一系列具体事物的共同点抽象出来, 再通过这个抽象的事物, 与不同的具体事物进行对话。

(3)对不同类的对象发出相同的消息将会有不同的行为。

比如,你的老板让所有员工在九点钟开始工作, 他只要在九点钟的时候说:“开始工作”即可,而不需要对销售人员说:“开始销售工作”,对技术人员说:“开始技术工作”, 因为“员工”是一个抽象的事物, 只要是员工就可以开始工作,他知道这一点就行了。至于每个员工,当然会各司其职,做各自的工作。

(4)多态允许将子类的对象当作父类的对象使用,某父类型的引用指向其子类型的对象,调用的方法是该子类型的方法。这里引用和调用方法的代码编译前就已经决定了,而引用所指向的对象可以在运行期间动态绑定。

(5)多态分为编译时多态(函数参数个数不同或者参数类型不同)和运行时多态(虚函数和纯虚函数)。

2、作用

(1)应用程序不必为每一个派生类编写功能调用,只需要对抽象基类进行处理即可。大大提高程序的可复用性。//继承

(2)派生类的功能可以被基类的方法或引用变量所调用,这叫向后兼容,可以提高可扩充性和可维护性。

        //多态的真正作用,以前需要用switch实现

(3)隐藏实现细节,使得代码能够模块化(虚函数)。

3、虚函数

class Student
{
    public:
        Student(){}
        ~Student(){}
    void 交学费(){}
        //......
};

里面有一个 “交学费”的处理函数,因为大学生和小学生一些情况类似,我们从小学生类中派生出大学生类:

class AcadStudent:public Student
{
    public:
        AcadStudent(){}
        ~ AcadStudent(){}
    void 交学费(){}
        //.......
};

中学生交费和大学生交费情况不同,所以虽然我们在大学生中继承了中学生的"交学费"操作,但我们不用这个继承来的操作,把它重载,定义大学生自己的交学费操作,这样当我们定义了一个小学生,一个大学生后:

Student A;
AcadStudent B;

A.交学费(); 即调用小学生的交学费操作,B.交学费();是调用大学生的交学费操作,功能是实现了,但是你要意识到,可能情况不仅这两种,可能N种如:小学生、初中生、高中生、研究生.....它们都可以以Student[小学生类]为基类。

如果系统要求你在一群这样的学生中,随便抽出一位交纳学费,你怎么做?

//A为抽出来的要交学费的同学
{
    switch(typeof(A))
    {
        case 小学生:
            A.小学生::交学费();
            break;
        case 初中生:
            A.初学生::交学费();break;
        case 高中生:
            A.高学生:: 交学费();break;
        default:
        ......
    }
}

首先,我们要在每个类中定义一个 typeof()用来识别它的类型,然后还要在程序中进行区别,这样一来,虽然也行,但是,如果再增加类型则要改动switch,又走了面向过程的老路,而且想通过一个模块进行操作实现起来也有难度。

所以C++中提供了多态,即能通过迟后联编的技术实现动态的区分

在基类的"交学费"前加个virtual 用来告诉系统,遇到这个处理过程要等到执行时再确定到底调用哪个类的处理过程。这样一来就可以:

void 通用的交学费操作 (Student &A)
{
    A.交学费();
}

一下全部搞定,你再加新的类型我也不怕。如果没有 virtual这一声明,那么,系统在执行前就确定了操作,比如把“大学生”传给:

void 通用的交学费操作(Student &A)
{
    A.交学费();
}

则A.交学费();调用的是大学生类中继承于Student类中的“交学费操作”,所以虚函数对多态的实现是必要的。

4、纯虚函数

class 人()
{
    public :
        //......
    void 吃()
    {
        人吃饭;
    }
    //......
    char *Name;
    int age;
};

class 狗()
{
    public :
    //......
    void 吃()
    {
        狗吃狗粮;
    }
    //......
    char *Name;
    int age;
};

人类、狗类有一些相同的属性,如名字,年纪,吃的动作等,有人想到了为了代码的重用,让人类继承狗类,可是数据的冗余让这个想法变得不可行,所以有人又想出定义一个这样的类:这个类界于人类狗类之间,有人类和狗类共有的一些东东,比如年纪,名字,体重什么的,但它是不存在实例的,它的存在意义也是只是为了派生其它类,如人类、狗类,这样可以使系统清淅。

在这个世界上根本不存在界于人狗之间的东西,所以这个“人狗之间类”中的“吃”也是没什么意义,你也很难为它的内容下个定义,况且也没必要,所以定义它为纯虚函数,形式为:virtual void 吃()=0; 用来告诉系统:

(1)这个函数可以没有函数定义

(2)拥有本函数的类是抽象类

即然纯虚函数没什么意义,为什么还要它,它的作用是什么呢?

——纯虚函数是为实现多态作准备

由于抽象类的实例没意义,所以C++中不允许定义它的实例。(如果定义了这样的实例A,那么你调用A.吃()怎么办?)

——纯虚函数在基类中是没有定义的(只是声明),必须在子类中加以实现;

——虚函数在子类里面也可以不重载的,但纯虚必须在子类去实现

你也可以在基类中,virtual 吃(){},这样基类就不是抽象类了,可以有实例,而且对于其它方面都不影响。但这样的对象是没有什么意义的,它的存在只能使你思考上增加负担,除错时还要考虑到是不是有这样类的对象在作怪,所以C++干脆提供了“虚函数”、抽象类的机制,给我们操作时加了限制也可以认为是保险(不可以有抽象类的对象),试想当代码变得非常之庞大时,它的存在是多么有必要啊!


© 著作权归作者所有

粉丝 9
博文 217
码字总数 158821
作品 0
南京
私信 提问

暂无文章

分布式协调服务zookeeper

ps.本文为《从Paxos到Zookeeper 分布式一致性原理与实践》笔记之一 ZooKeeper ZooKeeper曾是Apache Hadoop的一个子项目,是一个典型的分布式数据一致性的解决方案,分布式应用程序可以基于它...

ls_cherish
今天
4
0
redis 学习2

网站 启动 服务端 启动redis 服务端 在redis 安装目录下 src 里面 ./redis-server & 可以指定 配置文件或者端口 客户端 在 redis 的安装目录里面的 src 里面 ./redis-cli 可以指定 指定 连接...

之渊
昨天
2
0
Spring boot 静态资源访问

0. 两个配置 spring.mvc.static-path-patternspring.resources.static-locations 1. application中需要先行的两个配置项 1.1 spring.mvc.static-path-pattern 这个配置项是告诉springboo......

moon888
昨天
4
0
hash slot(虚拟桶)

在分布式集群中,如何保证相同请求落到相同的机器上,并且后面的集群机器可以尽可能的均分请求,并且当扩容或down机的情况下能对原有集群影响最小。 round robin算法:是把数据mod后直接映射...

李朝强
昨天
4
0
Kafka 原理和实战

本文首发于 vivo互联网技术 微信公众号 https://mp.weixin.qq.com/s/bV8AhqAjQp4a_iXRfobkCQ 作者简介:郑志彬,毕业于华南理工大学计算机科学与技术(双语班)。先后从事过电子商务、开放平...

vivo互联网技术
昨天
24
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部