对于经常被问到的虚函数和多态的问题,发现百度百科回答得十分详细,所以自己在百度百科上的解释进行总结

一、虚函数

(1)虚函数简介:在某基类中声明为virtual并在一个或者多个派生类中被重新定义的成员函数;实现多态性,通过指向派生类的基类指针或引用,访问派生类中同名覆盖成员函数。

(2)简单解释:被virtual关键字修饰的成员函数,就是虚函数。

(3)作用:实现多态性(polymorphism)。

(4)过程说明:

  先给定代码,对以下代码做说明:

 #include<iostream>
using namespace std;
class Animal
{
public:
void speak()
{
cout<<"I am a animal!"<<endl;
}
};
class Dog : public Animal
{
public:
void speak()
{
cout<<"I am a dog!"<<endl;
}
};
int main()
{
Animal animal;
Dog dog;
animal.speak();
dog.speak();
return ;
}

  输出结果:

  对于上面代码,通过class Animal和class Dog的speak( )函数接口,可以看出这两个class因个体差异而采取不同的行为,但是却没有满足多态性,因为多态性的关键是:一切用指向基类的指针或引用来操作对象。所以我们对main( )函数做修改,采用指向基类的指针或引用来调用:

 int main()
{
Animal animal;
Dog dog;
Animal *p1 = &animal;
Animal *p2 = &dog;
p1->speak();
p2->speak();
return ;

  输出结果:

  由输出可以看,两个调用输出的都是Animal自己的speak( )函数,这不符合我们的要求,所以引入了虚函数,来解决此问题;因此在基类的成员函数定义前加virtual:

 #include<iostream>
using namespace std;
class Animal
{
public:
virtual void speak()
{
cout<<"I am a animal!"<<endl;
}
};
class Dog : public Animal
{
public:
void speak()
{
cout<<"I am a dog!"<<endl;
}
};
int main()
{
Animal animal;
Dog dog;
Animal *p1 = &animal;
Animal *p2 = &dog;
p1->speak();
p2->speak();
return ;
}

  输出结果:

  作为基类的Animal的成员函数speak( )被定义为虚函数,相应的其派生类Dog的成员函数speak( )自动变为虚函数;所以对于派生类中相应成员函数是否加上virtual关键字修饰,是可选的,但是为了可读性,一般还是加上。

(5)限制条件:

  • 非类的成员函数不能定义为虚函数,类的成员函数中静态函数、构造函数也不能定义为虚函数,但是析构函数可以被定义为虚函数;
  • 当基类中的某一成员函数声明为虚函数后,派生类中的同名函数(函数名相同、参数列表完全一致、返回类型相关)自动成为虚函数;
  • 如果声明了某个成员函数为虚函数,则在该类中不能出现和这个成员函数同名并且返回值、参数个数、类型都相同的非虚函数;在以该类为基类的派生类中,也不能出现这种同名函数。

(6)总结:

  • 指向基类的指针在操作它的多态对象时,会根据不同的类对象,调用其相应的函数,这个函数就是虚函数;
  • 虚函数联系到多态,多态联系到继承。

二、多态性

  多态是指同一个实体同时具有多种形式。它是面向对象程序设计的一个重要特征。如果一个语言支持类而不支持多态,只能说明它是基于对象的,而不是面向对象的。

  C++中的多态性具体体现在运行和编译两个方面。

  • 运行时,为动态多态,具体引用的对象要在运行时才能确定;
  • 编译时,为静态多态,在编译时就可以确认对象使用的形式。

(1)定义:多态是指同一操作作用于不同的对象,可以有不同的解释,产生不同的执行结果。在运行时,可以通过指向基类的指针,来调用实现派生类中的方法。

(2)实现方法:C++中,实现多态可以通过虚函数、抽象类、覆盖、模板(重构与多态无关)。

(3)作用:

  把不同的子类对象都当作父类来看,可以屏蔽不同子类对象之间的差异,写出通用的代码,做出通用的编程,以适应需求的不断变化。

  赋值之后,父类型的引用就可以根据当前赋值给它的子对象的特性以不同的方式运作。也就是说,父亲的行为像儿子,而不是儿子的行为像父亲。

  举个例子:从一个基类中派生,响应一个虚命令,产生不同的结果。

  比如从某个基类派生出多个子类,其基类有一个虚方法Tdoit,然后其子类也有这个方法,但行为不同,然后这些子类对象中的任何一个可以赋给其基类对象的引用,或者说将子对象地址赋给基类指针,这样其基类的对象就可以执行不同的操作了。实际上你是在通过其基类的引用来访问其子类对象的,你要做的就是一个赋值操作。

  使用继承性的结果就是当创建了一个类的家族,在认识这个类的家族时,就是把子类的对象当作基类的对象,这种认识又叫作upcasting(向上转型)。这样认识的重要性在于:我们可以只针对基类写出一段程序,但它可以适应于这个类的家族,因为编译器会自动找出合适的对象来执行操作。这种现象又称为多态性。而实现多态性的手段又叫称动态绑定(dynamic binding)。

  简单的说,建立一个父类对象的引用,它所指对象可以是这个父类的对象,也可以是它的子类的对象。java中当子类拥有和父类同样的函数,当通过这个父类对象的引用调用这个函数的时候,调用到的是子类中的函数。

三、重构

  一种糟糕的现象:软件产品最初制造出来,是经过精心的设计,具有良好架构的。但是随着时间的发展、需求的变化,必须不断的修改原有的功能、追加新的功能,还免不了有一些缺陷需要修改。为了实现变更,不可避免的要违反最初的设计构架。经过一段时间以后,软件的架构就千疮百孔了。bug越来越多,越来越难维护,新的需求越来越难实现,软件的架构对新的需求渐渐的失去支持能力,而是成为一种制约。最后新需求的开发成本会超过开发一个新的软件的成本,这就是这个软件系统的生命走到尽头的时候。

  重构就能够最大限度的避免这样一种现象。系统发展到一定阶段后,使用重构的方式,不改变系统的外部功能,只对内部的结构进行重新的整理。通过重构,不断的调整系统的结构,使系统对于需求的变更始终具有较强的适应能力。

  重构可以降低项目的耦合度,使项目更加模块化,有利于项目的开发效率和后期的维护。让项目主框架突出鲜明,给人一种思路清晰,一目了然的感觉,其实重构是对框架的一种维护。

虚函数&多态的更多相关文章

  1. cc38b_demo_C++_异常_(2)txwtech在异常中使用虚函数-多态

    //cc38b_demo,21days_C++_异常_(2)txwtech20200121在异常中使用虚函数-多态 //--异常层次结构//*异常的类-创建自己的异常类//*异常派生-就是继承//*异 ...

  2. C++的静态分发(CRTP)和动态分发(虚函数多态)的比较

    虚函数是C++实现多态的工具,在运行时根据虚表决定调用合适的函数.这被称作动态分发.虚函数很好的实现了多态的要求,但是在运行时引入了一些开销,包括: 对每一个虚函数的调用都需要额外的指针寻址 虚函数通 ...

  3. (C/C++学习)5.C++中的虚继承-虚函数-多态解析

    说明:在C++学习的过程中,虚继承-虚函数经常是初学者容易产生误解的两个概念,它们与C++中多态形成的关系,也是很多初学者经常产生困惑的地方,这篇文章将依次分别对三者进行解析,并讲述其之间的联系与不同 ...

  4. c++ 虚函数多态、纯虚函数、虚函数表指针、虚基类表指针详解

    静态多态.动态多态 静态多态:程序在编译阶段就可以确定调用哪个函数.这种情况叫做静态多态.比如重载,编译器根据传递给函数的参数和函数名决定具体要使用哪一个函数.动态多态:在运行期间才可以确定最终调用的 ...

  5. 104.virtual虚函数多态与异构数据结构

    #include "mainwindow.h" #include <QApplication> #include <list> #include <Q ...

  6. C++:抽象基类和纯虚函数的理解

    转载地址:http://blog.csdn.net/acs713/article/details/7352440 抽象类是一种特殊的类,它是为了抽象和设计的目的为建立的,它处于继承层次结构的较上层. ...

  7. C++之虚函数和多态

    干货较多-需要自己深思理解: C++支持两种多态性: 1.编译时多态性(静态绑定-早绑定) 在程序编译阶段即可以确定下来的多态性 通过使用 重载机制(重载函数)实现 (模板)http://blog.c ...

  8. C++中的多态与虚函数的内部实现

    1.什么是多态         多态性可以简单概括为“一个接口,多种行为”.         也就是说,向不同的对象发送同一个消息, 不同的对象在接收时会产生不同的行为(即方法).也就是说,每个对象可 ...

  9. C# 类的多态、结构、接口、抽象、虚函数总结

    多态: 类的多态是通过在子类(派生类)中重载基类的虚方法或成员函数来实现的. 可见,重载和虚函数的重写,并在调用时用父类装箱子类对象,是实现多态的一种重要的编程方式. 接口: 接口是一种用来定义程序的 ...

随机推荐

  1. Redis多实例及主从搭建

    主从搭建前提是服务器上已经安装好了redis, redis安装可搜索本站另一篇博客:redis安装. redis单主机多实例 一.我们首先拷贝两份文件: cp /etc/redis.conf /etc ...

  2. 决策树模型比较:C4.5,CART,CHAID,QUEST

    (1)C4.5算法的特点为: 输入变量(自变量):为分类型变量或连续型变量. 输出变量(目标变量):为分类型变量. 连续变量处理:N等分离散化. 树分枝类型:多分枝. 分裂指标:信息增益比率gain ...

  3. js实现省市区三级联动

    电商平台或者一些网站的个人信息部分,通常会有填写地址的功能.该功能一般分为二级联动(省.市)和三级联动(省.市.区),只需要JavaScript就可以实现. 这里介绍一种很简洁易用的方法.参考地址:h ...

  4. 03 编译安装apache的简易配置

    1.监听端口,默认为80,在主配置文件 /etc/httpd24/httpd.conf中可以更改 2.持久连接 Include /etc/httpd24/extra/httpd-default.con ...

  5. 泛型(java菜鸟的课堂笔记)

                1. 泛型的简单运 用和意义   2. 泛型的上限与下限   3. 泛型和 子类继承的限制   4. 泛型类和泛型 方法   5. 泛型嵌套和泛型擦除             ...

  6. 容器常用操作 - 每天5分钟玩转 Docker 容器技术(25)

    前面讨论了如何运行容器,本节学习容器的其他常用操作. stop/start/restart 容器 通过 docker stop 可以停止运行的容器. 容器在 docker host 中实际上是一个进程 ...

  7. 搞不懂SSH与JAVA+Servlet+javabean有什么关系

    在SSH中:struts 是控制层,同时与Jsp结合代表表现层,同时负责客户端请求的处理,Spring主要处理逻辑事物处理,Hibernate主要对数据库的持久化操作. Jsp+Servlet+Jav ...

  8. 从netty源码里拿到的关于http错误码,自己学习下

    public static HttpResponseStatus valueOf(int code) { switch (code) { case 100: return CONTINUE; case ...

  9. Web压力测试软件webbench

    官方网站:http://home.tiscali.cz/~cz210552/webbench.html下载地址:http://home.tiscali.cz/~cz210552/distfiles/w ...

  10. Babel 入门指南

    Babel 入门指南 ​⚠️ 注意: Babel 可以与很多构建工具(如 Browserify.Grunt.Gulp 等)进行集成.由于本教程选择 Webpack ,所以只讲解与 Webpack 的集 ...