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 ...
随机推荐
- 计算机网络-ip地址聚合后可用的地址数
(1)59.81.1.128/28=59.81.1.1000-0000(2)59.81.1.144/28=59.81.1.1001-0000(3)59.81.1.160/28=59.81.1.1010 ...
- SQL Server 损坏修复
目录: 一. 常见错误解读 二. DBCC CHECKDB 三 .不同部位损坏的应对 四. Database Mirroring和AlwaysOn的页面自动修复功能 一 常见错误解读 SQL Serv ...
- UIAlertController使用的一个坑
/ // 创建一个确定按钮”一定要注意不能在提醒控制器的按钮的点击方法内部用到提醒控制器自己”,不能把下面这句话放在block内部”不然会死循环,导致警告控制器不能销毁" UITextFie ...
- AngularJS track by $index引起的思考
今天写了一段程序,只是一个简答的table数据绑定,但是绑定select的数据之后,发现ng-change事件失去了效果,不知道什么原因. 主要用到的代码如下: <div id="ri ...
- Gulp那些好用的插件 2016.04.20
开始接触LESS.组件化编程后,慢慢意识到需要一个提高工作效率的构建工具,就此接触到了Gulp. Gulp的好处在这里就不细说啦,只有四个API接口学起来简直爽歪歪,减少了大量的I/O操作,用起来很畅 ...
- Fibonacci 数列递归 重复计算
public class Fibonacci{ public static long F(long n){ System.out.println("call F" + n); ) ...
- SQLITE和QT
sqlite3数据库支持事务 例如: BEGIN DEFERRED TRANSACTION; INSERT INTO main.test_transaction (test_unique) VALUE ...
- 关于php的一些开源程序
最好用的当属thinksns,目前更新到4.3.4, 社交型网站. 此网站提供大量源码,有时间可以去看看:http://down.admin5.com/
- 无线端web开发学习总结
无线web开发之前要做一些准备工作:一.必需的reset样式库1.其中的重点是盒模型box-sizing:由原来pc端的content-box改为border-box. *, *:before, *: ...
- SQL Server2012连接SQL Server2000完美解决方案
在SQL Server2012中连接其他SQL Server数据库时可以使用以下代码: exec sp_addlinkedserver 'ITSV', '', 'SQLOLEDB', 'serveri ...