一、C++成员函数在内存中的存储方式

  用类去定义对象时,系统会为每一个对象分配存储空间。如果一个类包括了数据和函数,要分别为数据和函数的代码分配存储空间。按理说,如果用同一个类定义了10个对象,那么就需要分别为10个对象的数据和函数代码分配存储单元,如下图所示。

能否只用一段空间来存放这个共同的函数代码段,在调用各对象的函数时,都去调用这个公用的函数代码。如下图所示。

每个对象所占用的存储空间只是该对象的数据部分(虚函数指针和虚基类指针也属于数据部分)所占用的存储空间,而不包括函数代码所占用的存储空间

C/C++占用内存分为以下五种情况:

(1)栈区 : 存放局部变量 (由编译器自动分配和释放。)

(2)堆区:存放由malloc/new创建的对象(由程序员申请和释放。)

(3)全局区(静态区):存储全局变量和静态变量;程序结束后由系统释放。(初始化的全局变量和静态变量放在一起,未初始化的全局变量和静态变量存放在一起)

(4)常量区:存储常量字符串,程序结束后由系统释放;

(5)代码区:存放二进制代码;

二、虚函数表

编译器处理虚函数的方法是:为每个类对象添加一个隐藏成员,隐藏成员中保存了一个指向函数地址数组的指针,称为虚表指针(vptr),这种数组成为虚函数表(virtual function table, vtbl),即,每个类使用一个虚函数表,每个类对象用一个虚表指针

举个例子:基类对象包含一个虚表指针,指向基类中所有虚函数的地址表。派生类对象也将包含一个虚表指针,指向派生类虚函数表。看下面两种情况:

  • 如果派生类重写了基类的虚方法,该派生类虚函数表将保存重写的虚函数的地址,而不是基类的虚函数地址。

  • 如果基类中的虚方法没有在派生类中重写,那么派生类将继承基类中的虚方法,而且派生类中虚函数表将保存基类中未被重写的虚函数的地址。注意,如果派生类中定义了新的虚方法,则该虚函数的地址也将被添加到派生类虚函数表中。

======================================================================================

转自:https://www.cnblogs.com/alone-striver/p/7875741.html

1. 概述

简单地说,每一个含有虚函数(无论是其本身的,还是继承而来的)的类都至少有一个与之对应的虚函数表,其中存放着该类所有的虚函数对应的函数指针。例:

其中:

  • B的虚函数表中存放着B::foo和B::bar两个函数指针。
  • D的虚函数表中存放的既有继承自B的虚函数B::foo,又有重写(override)了基类虚函数B::bar的D::bar,还有新增的虚函数D::quz。

2. 虚函数表构造过程

从编译器的角度来说,B的虚函数表很好构造,D的虚函数表构造过程相对复杂。下面给出了构造D的虚函数表的一种方式(仅供参考):

提示:该过程是由编译器完成的,因此也可以说:虚函数替换过程发生在编译时。

3. 虚函数调用过程

以下面的程序为例:

编译器只知道pb是B*类型的指针,并不知道它指向的具体对象类型 :pb可能指向的是B的对象,也可能指向的是D的对象。

但对于“pb->bar()”,编译时能够确定的是:此处operator->的另一个参数是B::bar(因为pb是B*类型的,编译器认为bar是B::bar),而B::bar和D::bar在各自虚函数表中的偏移位置是相等的。

无论pb指向哪种类型的对象,只要能够确定被调函数在虚函数中的偏移值,待运行时,能够确定具体类型,并能找到相应vptr了,就能找出真正应该调用的函数。

B::bar是一个虚函数指针, 它的ptr部分内容为9,它在B的虚函数表中的偏移值为8(8+1=9)。

当程序执行到“pb->bar()”时,已经能够判断pb指向的具体类型了:

  • 如果pb指向B的对象,可以获取到B对象的vptr,加上偏移值8((char*)vptr + 8),可以找到B::bar。
  • 如果pb指向D的对象,可以获取到D对象的vptr,加上偏移值8((char*)vptr + 8) ,可以找到D::bar。
  • 如果pb指向其它类型对象...同理...

4. 多重继承

当一个类继承多个类,且多个基类都有虚函数时,子类对象中将包含多个虚函数表的指针(即多个vptr),例:

其中:D自身的虚函数与B基类共用了同一个虚函数表,因此也称B为D的主基类(primary base class)。

虚函数替换过程与前面描述类似,只是多了一个虚函数表,多了一次拷贝和替换的过程。

虚函数的调用过程,与前面描述基本类似,区别在于基类指针指向的位置可能不是派生类对象的起始位置,以如下面的程序为例:

最新文章

  1. 02_Swift2基础之常量和变量+注释+分号
  2. 算法——js(Fibonacci数列)
  3. Embedded System.
  4. swift-07-使用for-in 遍历数组
  5. coalesce和nvl函数
  6. AutoCompleteTextView 与sqlite绑定实现记住用户输入的内容并自动提示
  7. Linux下动态库使用
  8. c#XML配置文件辅助类
  9. 关于flask登录视图报错AttributeError: '_AppCtxGlobals' object has no attribute 'user'
  10. 来腾讯云开发者实验室 学习.NET
  11. Servlet.service() for Servlet jsp threw exception javax.servlet.ServletException:File "/pageFoo
  12. 【转】python3 urllib.request 网络请求操作
  13. man termios(FreeBSD 12.0)
  14. fastjson java类、字符串、jsonObject之前的转换
  15. export命令
  16. 第三百四十节,Python分布式爬虫打造搜索引擎Scrapy精讲—css选择器
  17. Ruby 面向对象知识详解
  18. Apache安装完后加入系统服务的相关操作详解
  19. 深入剖析PHP输入流 php://input
  20. bzoj2111 [ZJOI2010]排列计数

热门文章

  1. 6-9 Haar+adaboost人脸识别
  2. 任务39:Role以及Claims授权
  3. Ruby主要方法
  4. 更新gitignore
  5. Swift里计数相关的小细节
  6. git 命令参考手册
  7. LuoguP1268树的重量【构造/思维】By cellur925
  8. C++中virtual继承的深入理解
  9. 回文树1960. Palindromes and Super Abilities
  10. sql基础语法-创建表和约束