C++中的虚函数总结
一、什么是虚函数、纯虚函数、抽象基类
虚函数:在某基类中声明为 virtual 并在一个或多个派生类中被重新定 义的成员函数。
纯虚函数:是一种特殊的虚函数,使用virtual关键字,并且在其后面加上=0。
抽象基类:在基类中加入至少一个纯虚函数,使基类成为抽象类。
二、为什么要使用虚函数
在理解这个问题前,就必须要理解什么是晚捆绑。
晚捆绑是相对于早捆绑而言的,那么什么又是捆绑呢?把函数体与函数调用相联系称为捆绑,当捆绑在程序运行之前完成时,这称为早捆绑。那么当捆绑根据对象的类型,发生在运行时,就称为晚捆绑。
而使用晚捆绑,无需检查对象的类型,只需要检查对象是否支持特性和方法即可。
为了引发晚捆绑,C++要求在基类中声明这个函数时使用virture关键字。晚捆绑只对virtual函数起作用,而且只在使用含有virtual函数的基类的地址时发生。
三、关于重写
如果一个函数在基类中被声明为virtual,那么在所有的派生类中它都是virtual,在派生类中virtual函数的重定义通常称为重写。
四、在C++中如何实现晚捆绑
虚函数主要有两个步骤:
1、每一个类产生出一堆指向虚函数的指针,放在表格中。这个表格被称为virtual table(vtbl)
2、每一个类对象被安插一个指针,指向相关的virtual table,通常这个指针被称为vptr
结构图如下:
每当创建一个包含有虚函数的类或从包含有虚函数的类派生一个类时,编译器就为这个类创建一个唯一的vtbl。如果在这个派生类中没有对在基类中声明为virtual的函数进行重新定义,编译器就使用基类的这个虚函数地址。然后编译器在这个类中放置vptr。当使用简单继承时,对于每个对象都只有一个vtbl。vptr必须被初始化为指向相应的vtbl的起始地址。
五、虚函数的存放类型信息
假如没有虚函数,那么对象的长度就是所期望的长度:比如当个int的长度。而带有单个虚函数的One Virtual,对象的长度是No Virtual的长度加上一个void指针的长度。如果有一个或多个虚函数,编译器都只在这个结构中插入一个单个指针,这个指针指向虚函数表。在32为的机器上,一个指针占3字节的空间,因此求sizeof得到4;如果是64位的机器,一个指针占8字节的空间,因此求sizeof则得到8.
六、关于抽象类和纯虚函数
1、当继承一个抽象类时,必须实现所有的纯虚函数,否则继承出的类也将是一个抽象类
2、声明一个纯虚函数,就等于告诉编译器在vtbl中为函数保留一个位置,但在这个位置不放地址。只要有一个函数在类中被声明为纯虚函数,则vtbl就是不完全的。
3、纯虚函数禁止对抽象类的函数以传值方式调用,这是一种防止对象切片的方法。抽象类可以保证在向上类型转换期间总是使用指针或引用。
4、对于纯虚函数,如果要创建对象,必须要在派生类中定义。
七、什么是对象切片
在继承的过程中,通常派生类不仅具有基类的特征,也具有自身的一些特征。当派生类向上进行类型转换称为基类时,就会发生那些自身的特征被切除,只保留继承了基类的特征,这种现象就是对象切片。
例如:狗类继承了宠物类,具有宠物类的名称这个属性,同时又有啃骨头的特性,当狗类要被转换为宠物类时,就必须抛弃自己爱啃骨头的爱好,这样只保留了对应于宠物类的那部分。流程如下:
八、虚函数和构造函数
1、由于基类构造函数总是在继承类构造函数中被调用,这就确保了在派生类中,基类的所有成员都是有效的,即所有成员都已经建立。
2、虚机制在构造函数中不工作。有两种理由:
A、在任何构造函数中,我们只能知道基类已被初始化,但不能知道哪个类是从这个基类继承来的。但是,虚函数在继承层次上是向前和向后调用。它可以调用派生类中的函数。
B、构造函数的vptr的状态是由最后调用的构造函数确定的,这就意味着当最后调用的构造函数还没有完成之前,当前的构造函数完全不知道这个对象是否是基于其他类的。但是,当这一系列的构造函数调用正发生时,每个构造函数都已经设置vptr指向它自己的vtbl,如果函数调用使用虚机制,它将只产生通过它自己的vtbl的调用,而不是最后派生的vtbl。
九、虚析构函数和析构函数
1、析构函数自最晚派生的类开始,并向上到基类。这就意味着每个析构函数知道它所在类从哪一个类派生而来,但不知道从它派生出哪些类。
2、析构函数可以为虚函数,因为这个对象已经知道它是什么类型,但是在构造期间就不知道了。一旦对象已被构造,它的vptr就已经被初始化,所以能发生虚函数调用。
3、虚构函数的纯虚性的唯一效果是阻止基类的实例化
十、虚函数、纯虚函数、抽象类的作用
虚函数的作用:每个类必须提供一个可以被调用的虚函数,但每个类可以按它们认为合适的任何方式处理。如果某个类不想做什么特别的事,可以借助于基类中提供的缺省处理函数。也就是说,虚函数的声明是在告诉子类的设计者,"你必须支持虚函数,但如果你不想写自己的版本,可以借助基类中的缺省版本。
纯虚函数的作用:让所有的类对象(主要是派生类对象)都可以执行纯虚函数的动作,但类无法为纯虚函数提供一个合理的缺省实现。所以类纯虚函数的声明就是在告诉子类的设计者,“你必须提供一个纯虚函数的实现,但我不知道你会怎样实现它”。
抽象类的主要作用是将有关的操作作为结果接口组织在一个继承层次结构中,由它来为派生类提供一个公共的根,派生类将具体实现在其基类中作为接口的操作。所以派生类实际上刻画了一组子类的操作接口的通用语义,这些语义也传给子类,子类可以具体实现这些语义,也可以再将这些语义传给自己的子类。
C++中的虚函数总结的更多相关文章
- EC笔记,第二部分:9.不在构造、析构函数中调用虚函数
9.不在构造.析构函数中调用虚函数 1.在构造函数和析构函数中调用虚函数会产生什么结果呢? #; } 上述程序会产生什么样的输出呢? 你一定会以为会输出: cls2 make cls2 delete ...
- 关于在C#中构造函数中调用虚函数的问题
在C#中如果存在类的继承关系,应避免在构造函数中调用虚函数.这是由于C#的运行机制造成的,原因如下: 新建一个类实例时,C#会先初始化该类(对类变量赋值,并将函数记在函数表中),然后再初始化父类.构造 ...
- C++ 构造函数中调用虚函数
我们知道:C++中的多态使得可以根据对象的真实类型(动态类型)调用不同的虚函数.这种调用都是对象已经构建完成的情况.那如果在构造函数中调用虚函数,会怎么样呢? 有这么一段代码: class A { p ...
- C++中的虚函数解析[The explanation for virtual function of CPlusPlus]
1.什么是虚函数? ...
- C++箴言:避免构造或析构函数中调用虚函数
如果你已经从另外一种语言如C#或者Java转向了C++,你会觉得,避免在类的构造函数或者析构函数中调用虚函数这一原则有点违背直觉.但是在C++中,违反这个原则会给你带来难以预料的后果和无尽的烦恼. 正 ...
- 读书笔记 effective c++ Item 9 绝不要在构造函数或者析构函数中调用虚函数
关于构造函数的一个违反直觉的行为 我会以重复标题开始:你不应该在构造或者析构的过程中调用虚函数,因为这些调用的结果会和你想的不一样.如果你同时是一个java或者c#程序员,那么请着重注意这个条款,因为 ...
- C#中的虚函数及继承关系
转载:http://blog.csdn.net/suncherrydream/article/details/8423991 若一个实例方法声明前带有virtual关键字,那么这个方法就是虚方法. 虚 ...
- C++进阶--构造函数和析构函数中的虚函数
//############################################################################ /* 任何时候都不要在构造函数或析构函数中 ...
- 【校招面试 之 C/C++】第10题 C++不在构造函数和析构函数中调用虚函数
1.不要在构造函数中调用虚函数的原因 在概念上,构造函数的工作是为对象进行初始化.在构造函数完成之前,被构造的对象被认为“未完全生成”.当创建某个派生类的对象时,如果在它的基类的构造函数中调用虚函数, ...
- 【C++】C++中的虚函数与纯虚函数
C++中的虚函数 先来看一下实际的场景,就很容易明白为什么要引入虚函数的概念.假设我们有一个基类Base,Base中有一个方法eat:有一个派生类Derived从基类继承来,并且覆盖(Override ...
随机推荐
- 学习java随笔第七篇:java的类与对象
类 同一个包(同一个目录),类的创建与调用 class Man{ String name; void GetMyName() { System.out.println(name); } } publi ...
- [访问系统] C#计算机信息类ComputerInfo (转载)
下载整个包,只下载现有类是不起作用的 http://www.sufeinet.com/thread-303-1-1.html 点击此处下载 using System; using System.Man ...
- MongoDB的Document操作
简介 一.Document数据插入 二.Document数据删除 三.Document数据更新 一.Document数据插入 1.插入文档 db.[文档名].insert({BSON数据}) 2.批量 ...
- C#当中的多线程_线程池
3.1 简介 线程池主要用在需要大量短暂的开销大的资源的情形.我们预先分配一些资源在线程池当中,当我们需要使用的时候,直接从池中取出,代替了重新创建,不用时候就送回到池当中. .NET当中的线程池是受 ...
- 设置appicon和启动图
设置appicon General – App Icon Source 设置AppIcon 图片大小 iphone 6P(@3x) 180x180 iphone 6(@2x) iphone5/5s ( ...
- JavaScript Invalid Date Verify
if ( Object.prototype.toString.call(d) === "[object Date]" ) { // it is a date if ( isNaN( ...
- 【原创】CMD常用命令:解决实际问题
1.查找某一目录下后缀名文某某的所有文件. 命令格式:dir *.mp3 /a -d/b/s >C:\Users\leemo\Desktop\total.txt MP3为文件后缀名.>是命 ...
- 虚拟化技术与"云"
虚拟化技术: 如网站在某一时间访问量大,平时访问量少,如果一直保持大量的服务器提供服务,显示效率好低,浪费资源,在 不增减服务器,存储设备,网络等实际物理设备,而是利用软件将这些物理设备虚拟化,在有必 ...
- R 语言DataFrame 排序
Sort:dd <- data.frame(b = factor(c("Hi","Med","Hi","Low") ...
- css3多行省略号
-webkit-line-clamp 概述: -webkit-line-clamp 是一个 不规范的属性(unsupported WebKit property),它没有出现在 CSS 规范草案中. ...