虚函数&多态
对于经常被问到的虚函数和多态的问题,发现百度百科回答得十分详细,所以自己在百度百科上的解释进行总结
一、虚函数
(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越来越多,越来越难维护,新的需求越来越难实现,软件的架构对新的需求渐渐的失去支持能力,而是成为一种制约。最后新需求的开发成本会超过开发一个新的软件的成本,这就是这个软件系统的生命走到尽头的时候。
重构就能够最大限度的避免这样一种现象。系统发展到一定阶段后,使用重构的方式,不改变系统的外部功能,只对内部的结构进行重新的整理。通过重构,不断的调整系统的结构,使系统对于需求的变更始终具有较强的适应能力。
重构可以降低项目的耦合度,使项目更加模块化,有利于项目的开发效率和后期的维护。让项目主框架突出鲜明,给人一种思路清晰,一目了然的感觉,其实重构是对框架的一种维护。
虚函数&多态的更多相关文章
- cc38b_demo_C++_异常_(2)txwtech在异常中使用虚函数-多态
//cc38b_demo,21days_C++_异常_(2)txwtech20200121在异常中使用虚函数-多态 //--异常层次结构//*异常的类-创建自己的异常类//*异常派生-就是继承//*异 ...
- C++的静态分发(CRTP)和动态分发(虚函数多态)的比较
虚函数是C++实现多态的工具,在运行时根据虚表决定调用合适的函数.这被称作动态分发.虚函数很好的实现了多态的要求,但是在运行时引入了一些开销,包括: 对每一个虚函数的调用都需要额外的指针寻址 虚函数通 ...
- (C/C++学习)5.C++中的虚继承-虚函数-多态解析
说明:在C++学习的过程中,虚继承-虚函数经常是初学者容易产生误解的两个概念,它们与C++中多态形成的关系,也是很多初学者经常产生困惑的地方,这篇文章将依次分别对三者进行解析,并讲述其之间的联系与不同 ...
- c++ 虚函数多态、纯虚函数、虚函数表指针、虚基类表指针详解
静态多态.动态多态 静态多态:程序在编译阶段就可以确定调用哪个函数.这种情况叫做静态多态.比如重载,编译器根据传递给函数的参数和函数名决定具体要使用哪一个函数.动态多态:在运行期间才可以确定最终调用的 ...
- 104.virtual虚函数多态与异构数据结构
#include "mainwindow.h" #include <QApplication> #include <list> #include <Q ...
- C++:抽象基类和纯虚函数的理解
转载地址:http://blog.csdn.net/acs713/article/details/7352440 抽象类是一种特殊的类,它是为了抽象和设计的目的为建立的,它处于继承层次结构的较上层. ...
- C++之虚函数和多态
干货较多-需要自己深思理解: C++支持两种多态性: 1.编译时多态性(静态绑定-早绑定) 在程序编译阶段即可以确定下来的多态性 通过使用 重载机制(重载函数)实现 (模板)http://blog.c ...
- C++中的多态与虚函数的内部实现
1.什么是多态 多态性可以简单概括为“一个接口,多种行为”. 也就是说,向不同的对象发送同一个消息, 不同的对象在接收时会产生不同的行为(即方法).也就是说,每个对象可 ...
- C# 类的多态、结构、接口、抽象、虚函数总结
多态: 类的多态是通过在子类(派生类)中重载基类的虚方法或成员函数来实现的. 可见,重载和虚函数的重写,并在调用时用父类装箱子类对象,是实现多态的一种重要的编程方式. 接口: 接口是一种用来定义程序的 ...
随机推荐
- 01-.Net编程机制
.NetFarmwark特点: 多平台:该系统可以在广泛的计算机上运行,包括从服务器.桌面机到PDA和移动电话. 行业标准:该系统使用行业标准的通信协议,比如XML.HTTP.SOAP和WSDL. 安 ...
- 关于饿了么在浏览器标签页失去焦点时网页Title改变的实现方法
说在前面:必须是基于支持H5的浏览器才可以 这个 API 本身非常简单,由以下三部分组成. document.hidden:表示页面是否隐藏的布尔值.页面隐藏包括 页面在后台标签页中 或者 浏览器最小 ...
- shell脚本中$参数的介绍
$$Shell本身的PID(ProcessID)$!Shell最后运行的后台Process的PID$?最后运行的命令的结束代码(返回值)$-使用Set命令设定的Flag一览$*所有参数列表.如&quo ...
- MySQL开放远程登录
在服务器上部署MYSQL每次观看MYSQL记录或者修改的时候都需要登录服务器,又烦又占资源.所以使用另一种方法:对外开放接口. 注:如果某些服务器开启防火墙屏蔽了某些接口就有可能导致远程用户无法登录M ...
- Vue 犯罪指南:TypeError: Right-hand side of 'instanceof' is not an object
背景:在迁移项目从 Vue1.x 到 Vue2.x 时,改动没有过脑子,导致翻车.
- div的替代品
人们在标签使用中最常见到的错误之一就是随意将HTML5的<section>等价于<div>--具体地说,就是直接用作替代品(用于样式).在XHTML或者HTML4中,我们常看到 ...
- phpcms插件
最近在研究PHP,学了一段时间后感觉想自己写点东西,但是又不知道写什么,最后把目标对准了PHPCMS的插件,从网上找了一下,大部分教程都只是教怎么写插件的具体代码,而没有教怎么把插件打成安装包,特别是 ...
- jquery页面水印插件,支持多行水印、行错开
最近工作需求,需要在页面上加水印,但发现网上示例无法满足我的需求,所以还是自己动手写. 有几个特别需求: 1.可以写多行水印,并且中心对齐. 2.每行水印错开. PS:我找到的例子都是单行水印,所以用 ...
- orcle :Could not initialize "D:\app\Administrator\product\11.2.0\dbhome_1\bin\oci.dll" Make sure you have the 32 bits Oracle Client installed.
服务器重启后,数据库登录信息为空 错误信息: ---------------------------(Not logged on) - PL/SQL Developer---------------- ...
- Ubuntu下解决解压zip文件中文文件名乱码问题
在Ubuntu下解压Windows下压缩的zip文件时,会出现解压出的带中文文件名的文件名乱码,这是因为Ubuntu和Windows默认的编码不同,Ubuntu下默认的编码是UTF-8,而Window ...